blazor

Maximum allowed size of OpenReadStream


What is the maximum allowed size of OpenReadStream? Right now, using 10MB. But I believe there has to be a certain cap. Can it take GB?


Solution

  • In ASP.NET Core 5.0, there was a 2 GB framework file size limit for uploading files. However, starting with ASP.NET Core 6.0 and later versions, the framework doesn't impose a maximum file size limit.

    By default, files no larger than 512,000 bytes (500 KB) in size are allowed to be read before any further reads would result in an exception. This limit is present to prevent developers from accidentally reading large files into memory.

    // Uses the default max allowed size of 500 KB (512,000 bytes)
    await myFile.OpenReadStream().ReadAsync(buffers);
    

    To set a custom file upload size, you can override the maxAllowedSize parameter in the OpenReadStream method, as shown below:

    Example for 300 KB limit:
    // Accept a file up to 307200 bytes (300 KB) in size
    await myFile.OpenReadStream(maxAllowedSize: 1024 * 300).ReadAsync(buffers);
    
    Example for 2 GB limit:
    // Define the maximum file size allowed (2 GB)
    long maxFileSize = 2L * 1024L * 1024L * 1024L;
    
    // Accept a file up to the specified size
    await myFile.OpenReadStream(maxAllowedSize: maxFileSize).ReadAsync(buffers);
    

    For smaller file sizes less than 2GB, using int is sufficient. However, for larger file sizes, use long to avoid integer overflow. The L suffix in 2L and 1024L explicitly declares these values as 64-bit long integers, ensuring accurate calculations for large file sizes.

    The OpenReadStream method can indeed handle files in the gigabyte range. The maximum allowed size is limited only by the available system resources and any application-specific constraints you set.


    UPDATE: I tested the file upload with a file that is 2 GB in size, and it was uploaded successfully.

    DEMO:

    Image showing upload of 2GB file using Blazor Server

    CODE:
    This Blazor Server code example demonstrates uploading large files:

    @page "/"
    @using System.IO
    @using System.Buffers
    @inject IWebHostEnvironment Env
    
    <h1 class="mb-4">Blazor Server File Upload</h1>
    
    <!-- Alert message display -->
    <div class="@AlertClass" role="alert">
        @AlertMessage
    </div>
    
    <!-- Progress bar for file upload -->
    <div class="progress mb-3" style="height: 20px;">
        <div class="progress-bar" role="progressbar" style="width: @ProgressPercentage%;" aria-valuenow="@ProgressPercentage" aria-valuemin="0" aria-valuemax="100">@ProgressPercentage% Complete</div>
    </div>
    
    <!-- File upload form -->
    <form @onsubmit="OnSubmitAsync" class="needs-validation" novalidate>
        <div class="input-group mb-3">
            <InputFile @key="InputFileId" class="form-control" id="inputFile" OnChange="OnInputFileChange" aria-describedby="uploadButton" required />
            <button class="btn btn-primary" type="submit" id="uploadButton" disabled="@IsUploadDisabled">
                <span class="oi oi-cloud-upload" aria-hidden="true"></span> Upload Selected File
            </button>
        </div>
        <div class="invalid-feedback">
            Please select a file to upload.
        </div>
    </form>
    
    @code {
        // Constants for file size and allowed extensions
        private const long MaxFileSize = 2L * 1024L * 1024L * 1024L; // 2 GB
        private static readonly string[] AllowedExtensions = { ".zip", ".rar", ".bin" };
    
        // Properties for UI state management
        private MarkupString AlertMessage { get; set; } = new("<strong>No file selected</strong>");
        private string AlertClass { get; set; } = "alert alert-info";
        private int ProgressPercentage { get; set; }
        private IBrowserFile? SelectedFile { get; set; }
        private bool IsUploadDisabled { get; set; } = true;
        private Guid InputFileId { get; set; } = Guid.NewGuid();
    
        // Handle file selection
        private void OnInputFileChange(InputFileChangeEventArgs e)
        {
            SelectedFile = e.GetMultipleFiles().FirstOrDefault();
            ProgressPercentage = 0;
            IsUploadDisabled = true;
    
            // Validate selected file
            if (SelectedFile is null)
            {
                SetAlert("alert alert-warning", "oi oi-warning", "No file selected.");
                return;
            }
    
            if (SelectedFile.Size > MaxFileSize)
            {
                SetAlert("alert alert-danger", "oi oi-ban", $"File size exceeds the limit. Maximum allowed size is <strong>{MaxFileSize / (1024 * 1024)} MB</strong>.");
                return;
            }
    
            string fileExtension = Path.GetExtension(SelectedFile.Name).ToLowerInvariant();
            if (!AllowedExtensions.Contains(fileExtension))
            {
                SetAlert("alert alert-danger", "oi oi-warning", $"Invalid file type. Allowed file types are <strong>{string.Join(", ", AllowedExtensions)}</strong>.");
                return;
            }
    
            SetAlert("alert alert-info", "oi oi-info", $"<strong>{SelectedFile.Name}</strong> ({SelectedFile.Size} bytes) file selected.");
            IsUploadDisabled = false;
        }
    
        // Handle file upload
        private async Task OnSubmitAsync()
        {
            if (SelectedFile is null) return;
    
            IsUploadDisabled = true;
            await using Stream stream = SelectedFile.OpenReadStream(MaxFileSize);
            string path = Path.Combine(Env.WebRootPath, SelectedFile.Name);
            await using FileStream fs = File.Create(path);
    
            // Set up buffer for file reading
            const int bufferSize = 512 * 1024; // 512 KB
            byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
            long totalBytesRead = 0;
            long fileSize = SelectedFile.Size;
    
            // Set up timer for UI updates
            using var timer = new Timer(_ => InvokeAsync(StateHasChanged));
            timer.Change(TimeSpan.FromMilliseconds(500), TimeSpan.FromMilliseconds(500));
    
            try
            {
                // Read and write file in chunks
                int bytesRead;
                while ((bytesRead = await stream.ReadAsync(buffer)) != 0)
                {
                    totalBytesRead += bytesRead;
                    ProgressPercentage = (int)(100 * totalBytesRead / fileSize);
                    await fs.WriteAsync(buffer.AsMemory(0, bytesRead));
                }
            }
            finally
            {
                ArrayPool<byte>.Shared.Return(buffer);
            }
    
            // Clean up and update UI
            timer.Change(Timeout.Infinite, Timeout.Infinite);
            ProgressPercentage = 100;
            SetAlert("alert alert-success", "oi oi-check", $"<strong>{SelectedFile.Name}</strong> ({SelectedFile.Size} bytes) file uploaded on server.");
            InputFileId = Guid.NewGuid();
            StateHasChanged();
        }
    
        // Helper method to set alert message and style
        private void SetAlert(string alertClass, string iconClass, string message)
        {
            AlertClass = alertClass;
            AlertMessage = new MarkupString($"<span class='{iconClass}' aria-hidden='true'></span> {message}");
        }
    }
    

    Important information about this code example: