Upload files

DotVVM contains the FileUpload control which implements the asynchronous file upload mechanism.

The upload works on the background and starts immediately when the user selects the files. The progress is reported to the user (via a CSS-styleable user interface), and the user can work with the page while the files are being uploaded.

When the upload is complete, unique IDs of the files stored on the server are written in a UploadedFilesCollection object in the viewmodel, which can be used to access the file contents.

Upload configuration

To make file uploads work, you need to specify where the temporary files will be uploaded.

The recommended strategy is to store the uploaded files in your application directory, or in some temp directory (if your app have the appropriate permissions).

The default project template specifies the default configuration of the uploaded files storage in DotvvmStartup.cs. It registers a storage for uploaded files, as well as a storage for returned files.

public void ConfigureServices(IDotvvmServiceCollection options)
{
    options.AddDefaultTempStorage("temp");

    // this is equivalent to registering both uploaded and returned file storage

    // options.AddUploadedFileStorage("temp");
    // options.AddReturnedFileStorage("temp");
}

The default storage creates a temp folder in your app directory, and stores uploaded files there. In order to prevent unlimited growth of this folder, the files are deleted after 30 minutes. You can change the default timeout to some other value:

public void ConfigureServices(IDotvvmServiceCollection options)
{
    options.AddDefaultTempStorage("temp", TimeSpan.FromMinutes(10));
}

Alternatively, you can provide a different implementation of IUploadedFileStorage - for example store files in Azure Blob Storage.

public void ConfigureServices(IDotvvmServiceCollection options)
{
    options.Services.AddSingleton<IUploadedFileStorage, MyCustomUploadedFileStorage>();
}

UploadedFilesCollection

The FileUpload control needs to specify a UploadedFilesCollection.

This object contains several properties:

  • Files property is a list with entries for all uploaded files
    • FileId property contains a unique id of the file on the server
    • FileName property contains a name of the file on the user's computer
    • FileSize provides info about the size of the file (number of bytes, and a human-readable value representing the file size )
  • IsBusy and Progress properties indicate whether something is being uploaded at the moment, and the progress in percents (0 to 100)
  • Error property contains an error message indicating if there was a problem during the upload

While files are being uploaded, DotVVM regularly updates the contents of the collection.

Process the stored files

The files are saved to a temporary location on the server. The UploadedFilesCollection holds only unique IDs of the files.

To access the file contents, you need to retrieve them using the IUploadedFileStorage object.

The simplest way to interact with IUploadedFileStorage service is to request it as a parameter in the viewmodel constructor.

public class MyViewModel : DotvvmViewModelBase
{

    private readonly IUploadedFileStorage storage;

	  public MyViewModel(IUploadedFileStorage storage)
	  {
	      this.storage = storage;
	  }
	
	  ...
}

You can then use the GetFileAsync method to retrieve the Stream to access the file contents.

foreach (var file in UploadedFiles.Files)
{
    // get the stream of the uploaded file and do whatever you need to do
    var stream = await storage.GetFileAsync(file.FileId);

    // OR you can just move the file from the temporary storage somewhere else
    await storage.SaveAsAsync(file.FileId, "some-target-path/myfile.bin");
    
    // it is a good idea to delete the file from the temporary storage 
    // the file would be deleted automatically after the timeout set in the DotvvmStartup.cs (default is 30 minutes)
    await storage.DeleteFileAsync(file.FileId);
}

Be very careful if you use the FileName property - an attacker may tamper with the file name in attempt to submit executable code to your server to a location where it will be executed. We recommend to always generate file names on your own, and use the FileName property supplied by the user only in the Content-Disposition header when you want to allow the file be downloaded later.

The FileUpload control allows to restrict the uploaded files to only specific content types, and also allows to limit the size of the file. However, the check happens in the browser, so you should always validate files that come to your application.

Request body limits in ASP.NET

When files are uploaded, the default limits of the ASP.NET platform come in place.

In ASP.NET Core, there are two limits which can prevent the files from being uploaded:

You can change the limit in Program.cs:

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureKestrel((context, options) =>
        {
            // Handle requests up to 50 MB
            options.Limits.MaxRequestBodySize = 52428800;
        });

You can change the limit in the ConfigureServices method in Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    ...

    services.Configure<FormOptions>(options =>
    {
        // Set the limit to 256 MB
        options.MultipartBodyLengthLimit = 268435456;
    });
}
  • If you are running on IIS, make sure to configure the request filtering limit too:
<configuration>
  <system.webServer>
    <security>
      <requestFiltering>
        <!-- 256 -->
        <requestLimits maxAllowedContentLength="268435456" /> // specified in bytes
      </requestFiltering>
    </security>
  </system.webServer>
</configuration>

If you have problems uploading large files, check if there are any other limits on request length imposed by the web server or a reverse proxy.

See also