dotnet
nextjs
react
tus
In this post we will see how to do large file upload in chunks with resume capabilities in dotnet core as a backend and react/nextjs as frontend.
1dotnet new webapp -n FileUpload -o .
1dotnet add package tusdotnet --version 2.4.0
Update the ConfigureServices
1services.AddCors();
Update Configure
method
1app.UseCors(builder => builder 2 .AllowAnyHeader() 3 .AllowAnyMethod() 4 .AllowAnyOrigin() 5 .WithExposedHeaders(CorsHelper.GetExposedHeaders()));
Update Configure
method and make sure this is the first pipeline request.
1app.Use((context, next) => 2{ 3 // Default limit was changed some time ago. Should work by setting MaxRequestBodySize to null using ConfigureKestrel but this does not seem to work for IISExpress. 4 // Source: https://github.com/aspnet/Announcements/issues/267 5 context.Features.Get<IHttpMaxRequestBodySizeFeature>().MaxRequestBodySize = null; 6 return next.Invoke(); 7});
Update Configure
method
1app.UseTus(httpContext => new DefaultTusConfiguration 2{ 3 Store = new TusDiskStore(@"C:\tusfiles\"), 4 UrlPath = "/files", 5 Events = new Events 6 { 7 OnFileCompleteAsync = async eventContext => 8 { 9 // eventContext.FileId is the id of the file that was uploaded. 10 // eventContext.Store is the data store that was used (in this case an instance of the TusDiskStore) 11 12 // A normal use case here would be to read the file and do some processing on it. 13 ITusFile file = await eventContext.GetFileAsync(); 14 var result = await DoSomeProcessing(file, eventContext.CancellationToken).ConfigureAwait(false); 15 16 if (!result) 17 { 18 //throw new MyProcessingException("Something went wrong during processing"); 19 } 20 } 21 } 22});
Make sure to put this before UseRouting
and UseAuthorization
Create next app
1yarn create next-app --typescript
Add tus package
1yarn add tus-js-client
Create file upload component
1import React from "react"; 2import { Upload } from "tus-js-client"; 3interface Props {} 4 5const FileUpload = (props: Props) => { 6 const onFileChange = (e: any) => { 7 var file = e.target.files[0]; 8 const upload = new Upload(file, { 9 endpoint: "https://localhost:5001/files", 10 retryDelays: [0, 1000, 3000, 5000], 11 metadata: { 12 filename: file.name, 13 filetype: file.type, 14 }, 15 onError: function (error) { 16 console.log("Failed because: " + error); 17 }, 18 onProgress: function (bytesUploaded, bytesTotal) { 19 var percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2); 20 console.log(bytesUploaded, bytesTotal, percentage + "%"); 21 }, 22 onSuccess: function () { 23 console.log("Download %s from %s", upload.file.name, upload.url); 24 }, 25 }); 26 27 // Check if there are any previous uploads to continue. 28 upload.findPreviousUploads().then(function (previousUploads) { 29 // Found previous uploads so we select the first one. 30 if (previousUploads.length) { 31 upload.resumeFromPreviousUpload(previousUploads[0]); 32 } 33 34 // Start the upload 35 upload.start(); 36 }); 37 }; 38 return ( 39 <div className="text-center text-2xl p-6"> 40 <input type="file" name="file" id="" onChange={onFileChange} /> 41 </div> 42 ); 43}; 44 45export default FileUpload;
Add the component to the page.
1<FileUpload></FileUpload>
Github Repo: https://github.com/antosubash/LargeFileUploadSample