SolidTUS 0.0.18

There is a newer version of this package available.
See the version list below for details.
dotnet add package SolidTUS --version 0.0.18
NuGet\Install-Package SolidTUS -Version 0.0.18
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SolidTUS" Version="0.0.18" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add SolidTUS --version 0.0.18
#r "nuget: SolidTUS, 0.0.18"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install SolidTUS as a Cake Addin
#addin nuget:?package=SolidTUS&version=0.0.18

// Install SolidTUS as a Cake Tool
#tool nuget:?package=SolidTUS&version=0.0.18

alternate text is missing from this package README image

What is it?

A C# dotnet 7 implementation of the TUS-protocol for resumable file uploads for an ASP.NET Core application.

Why create another TUS library?
This library's purpose is to be simple and give the consumer more options on how to authorize and authenticate requests.
Which I felt that other libraries did not provide.
SolidTUS is a more Controller/Action oriented and integrates well with a Web API or an MVC project.

If you have any suggestions or improvements please do not hesitate to contribute.

Current TUS features

SolidTUS currently implements the following.

Basic features:

  • Core-protocol v.1.0.0 (stop and resume uploads)
  • Max-size (define a hard limit for upload size)
  • Tus-metadata validation
  • Options (server feature announcements)

Extensions:

Note that the checksum feature does not implement the trailing header feature, i.e. A checksum value must be provided upon sending the http request.

Only termination announcement in OPTION request is implemented.
The actual functionality must be implemented by yourself. See examples and documentation on how to and why.

Future goals is to implement all the extensions:

Other TUS libraries for C#

Quickstart

Add the package to your project:

$ `dotnet add package SolidTUS`

Register the service in the startup process:

// Register TUS services
builder.Services.AddTUS();

In your Controller add the TusCreation-attribute to the action method endpoint and the TusCreationContext as parameter.

[TusCreation]
public async Task<ActionResult> CreateUpload([FromServices] TusCreationContext context)
{
  // ... Construct a file ID and an URL route to the Upload endpoint
  var fileID = "myFileID";
  var url = "/url/path/to/upload/action/myFileID";
  
  // When ready to create resource metadata
  await context.StartCreationAsync(fileID, url);
  
  return NoContent();
}

This will not upload any file (unless the client explicitly uses the TUS-extension Creation-With-Upload feature).
This only sets the ground work for getting information such as file size, and where to upload the data.

Next the actual upload.

Set the TusUpload-attribute and add the TusUploadContext as a parameter

[TusUpload]
[Route("url/path/to/upload/action/{fileId}")]
public async Task<ActionResult> Upload(string fileId, [FromServices] TusUploadContext context)
{
  // ... stuff
  await context.StartAppendDataAsync(fileId);
  
  // Important must return 204 on success (TUS-protocol)
  return NoContent();
}

And done...

Extra options

Configurations

TUS configurations

// Custom metadata provider or set maximum TUS protocol file size
builder.Services
  .AddTUS()
  .Configuration(options =>
  {
    // A Func<string, bool> that validates the given TUS-metadata upon resource creation
    options.MetadataValidator = (metadata) => metadata.ContainsKey("filename");
    
    // This max size is different than the ASP.NET specified max size. To change the request size limit do it per Action with an attribute (recommended).
    options.MaxSize = 5_000_000_000;
  });

Note: to change request size limits see: Microsoft documentation

If you don't want to use the default FileUploadStorageHandler you can provide your own, maybe you want to save the files to a database?

// Add custom storage handler
builder.Services
  .AddTUS()
  .AddStorageHandler<MyStorageHandler>(); // <-- must implement IUploadStorageHandler interface

If you want to support more checksum validators. The default checksum validators are: SHA1 and MD5:

// Add custom checksum validator
builder.Services
  .AddTUS()
  .AddChecksumValidator<MyChecksumValidator>(); // <-- must implement IChecksumValidator interface

If you use the default FileUploadStorageHandler you can configure the directory where to store files:

// Add custom checksum validator
builder.Services
  .AddTUS()
  .FileStorageConfiguration(options =>
  {
    options.DirectoryPath = "path/to/where/save/upload/files";
  });

another option is to determine where each file should be uploaded on per upload basis. In the Controller you can specify the file path:

[TusUpload]
public async Task<ActionResult> Upload(string fileId, [FromServices] TusUploadContext context)
{
    // ... omitted
    await context.StartAppendDataAsync(fileId, "determine/path/per/upload");
    // ... omitted
}

Configuration from appSettings.json or environment variables

You can configure the Tus-Max-Size parameter and the default file storage upload folder from the appSettings.json configuration:

{
  "SolidTUS": {
    "DirectoryPath": "/path/to/my/uploads",
    "MaxSize": "3000000"
  }
}

Environment variables are named as SolidTUS__DirectoryPath with a double underscore (so they also can be read from a linux environment). See Microsofts documentation for naming

Contexts

The injected context classes are excluded from ModelBinding but do show up in Swagger SwashBuckle. To exclude from Swagger SwashBuckle you can annotate the contexts with the FromServices-attribute.

IMPORTANT: Callbacks must be defined before upload starts.
The StartCreationAsync and StartAppendDataAsync starts upload.

TusUploadContext

Is responsible for starting or terminating the upload. A termination is a premature ending and signals to the client that the upload has been terminated.

The class contains the following members:

  • OnUploadFinished - A method that takes an awaitable callback. When the whole file has been completely uploaded the callback is invoked.
  • StartAppendDataAsync - Starts accepting the upload stream from the client
  • TerminateUpload - Returns an error http response (default: 400 BadRequest)

MUST call either StartAppendDataAsync or TerminateUpload method. Cannot call both in a single request (you can't accept and not accept an upload).

The TusUploadContext is injected from the TusUpload-attribute.

TusUploadAttribute

Is responsible for marking the TUS-protocol upload endpoint. And needs information about 2 things:

  1. The file ID parameter name of type string
  2. The context parameter name of type TusUploadContext

These parameters can be tuned:

[TusUpload(FileIdParameterName = "Id", ContextParameterName = "tus")]
public async Task<ActionResult> UploadEndPoint(string Id, TusUploadContext tus)
{
  /* Logic omitted ... */
}

TusCreationContext

Is responsible for creating the resource metadata UploadFileInfo. Defining the file ID and eventual any TUS-metadata.
SolidTUS implements the TUS-protocol extension creation-with-upload thus a resource creation can contain some upload data.
Before reaching the actual Action method the metadata validator defined in the Configuration will run; If metadata is invalid an automatic response of 400 Bad Request will be returned, as specified in the TUS-protocol.
The class contains the following members:

  • StartCreationAsync - Starts resource creation
  • OnResourceCreated - A method that takes a callback function, which is invoked when the resource has been successfully created
  • OnUploadFinished - A method that takes a callback function, which is invoked when the partial file or whole file has finished uploading. It could be the client has sent a partial upload or the whole file.
  • Metadata - A Dictionary<string, string> property of the parsed TUS-metadata

The TusCreationContext is injected from the TusCreation-attribute.

TusCreationAttribute

Is responsible for marking the TUS-creation endpoint and needs information about the context parameter name.

[TusCreation(ContextParameterName = "creationContext")]
public async Task<ActionResult> CreationEndPoint(TusUploadContext creationContext)
{
  /* Logic omitted ... */
}

The TUS protocol with SolidTUS simplified

In essence the client sends a request to an endpoint (as marked by the TusCreation attribute:

POST /files HTTP/1.1
Host: tus.example.org
Content-Length: 0
Upload-Length: 100
Tus-Resumable: 1.0.0

The server then knows to expect an upload file with a total size of 100 bytes and at which point SolidTUS creates an UploadFileInfo which contains this metadata.
The server responds on success where the file can be uploaded:

HTTP/1.1 201 Created
Location: https://tus.example.org/files/24e533e02ec3bc40c387f1a0e460e216
Tus-Resumable: 1.0.0

In this example the file has an ID of 24e533e02ec3bc40c387f1a0e460e216.
The client then uploads the data to that location as marked by the TusUpload attribute:

PATCH /files/24e533e02ec3bc40c387f1a0e460e216 HTTP/1.1
Host: tus.example.org
Content-Type: application/offset+octet-stream
Content-Length: 30
Upload-Offset: 70
Tus-Resumable: 1.0.0

[remaining 30 bytes]

After some while the upload starts from byte 70 and has a total size of 100, the missing 30 bytes are in the PATCH body. This data is directed to the OnPartialUploadAsync method in the IUploadStorageHandler.

When finished successfully the server responds:

HTTP/1.1 204 No Content
Tus-Resumable: 1.0.0
Upload-Offset: 100

Test methodology

Using unit tests and manually making TUS-request with the official javascript client in the examples folder.

TODO

  • Create wiki pages for all the configuration options
  • Create wiki pages for library design, and how to extend
  • Implement all TUS extension features

References

Product Compatible and additional computed target framework versions.
.NET net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net7.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.0.34 95 2/2/2024
0.0.33 83 2/1/2024
0.0.32 109 1/12/2024
0.0.31 112 1/4/2024
0.0.30 104 1/4/2024
0.0.29 147 1/3/2024
0.0.28 99 1/2/2024
0.0.27 96 12/21/2023
0.0.26 94 12/18/2023
0.0.25 82 12/11/2023
0.0.24 73 12/11/2023
0.0.23 65 12/11/2023
0.0.22 87 12/7/2023
0.0.21 99 12/5/2023
0.0.20 110 10/9/2023
0.0.19 111 9/25/2023
0.0.18 118 9/23/2023
0.0.17 142 4/27/2023
0.0.16 387 8/8/2022
0.0.15 365 8/6/2022
0.0.14 380 8/5/2022
0.0.13 379 7/21/2022
0.0.12 368 7/21/2022
0.0.11 368 7/21/2022
0.0.10 396 7/13/2022
0.0.9 409 7/13/2022
0.0.8 397 7/13/2022
0.0.7 398 7/12/2022