// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.Use((context, next) => { // 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. // Source: https://github.com/aspnet/Announcements/issues/267 context.Features.Get <IHttpMaxRequestBodySizeFeature>().MaxRequestBodySize = null; return(next.Invoke()); }); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseDefaultFiles(); app.UseStaticFiles(); app.UseCors(builder => builder .AllowAnyHeader() .AllowAnyMethod() .AllowAnyOrigin() .WithExposedHeaders(CorsHelper.GetExposedHeaders())); //app.UseSimpleExceptionHandler(); // httpContext parameter can be used to create a tus configuration based on current user, domain, host, port or whatever. // In this case we just return the same configuration for everyone. app.UseTus(httpContext => Task.FromResult(httpContext.RequestServices.GetService <DefaultTusConfiguration>())); // All GET requests to tusdotnet are forwarded so that you can handle file downloads. // This is done because the file's metadata is domain specific and thus cannot be handled // in a generic way by tusdotnet. //app.UseSimpleDownloadMiddleware(httpContext => Task.FromResult(httpContext.RequestServices.GetService<DefaultTusConfiguration>())); }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseDefaultFiles(); app.UseStaticFiles(); app.UseCors(builder => builder .AllowAnyHeader() .AllowAnyMethod() .AllowAnyOrigin() .WithExposedHeaders(CorsHelper.GetExposedHeaders())); app.UseSimpleExceptionHandler(); // httpContext parameter can be used to create a tus configuration based on current user, domain, host, port or whatever. // In this case we just return the same configuration for everyone. app.UseTus(httpContext => Task.FromResult(httpContext.RequestServices.GetService <DefaultTusConfiguration>())); // All GET requests to tusdotnet are forwarded so that you can handle file downloads. // This is done because the file's metadata is domain specific and thus cannot be handled // in a generic way by tusdotnet. app.UseSimpleDownloadMiddleware(httpContext => Task.FromResult(httpContext.RequestServices.GetService <DefaultTusConfiguration>())); }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(); app.UseDefaultFiles(); app.UseStaticFiles(); app.UseCors(builder => builder .AllowAnyHeader() .AllowAnyMethod() .AllowAnyOrigin() .WithExposedHeaders(CorsHelper.GetExposedHeaders())); app.UseSimpleExceptionHandler(); // httpContext parameter can be used to create a tus configuration based on current user, domain, host, port or whatever. // In this case we just return the same configuration for everyone. app.UseTus(httpContext => Task.FromResult(httpContext.RequestServices.GetService <DefaultTusConfiguration>())); // All GET requests to tusdotnet are forwarded so that you can handle file downloads. // This is done because the file's metadata is domain specific and thus cannot be handled // in a generic way by tusdotnet. app.UseSimpleDownloadMiddleware(httpContext => Task.FromResult(httpContext.RequestServices.GetService <DefaultTusConfiguration>())); // Start cleanup job to remove incomplete expired files. var cleanupService = app.ApplicationServices.GetService <ExpiredFilesCleanupService>(); cleanupService.Start(); }
private void SetupCorsPolicy(IAppBuilder app) { var corsPolicy = new System.Web.Cors.CorsPolicy { AllowAnyHeader = true, AllowAnyMethod = true, AllowAnyOrigin = true }; corsPolicy.GetType() .GetProperty(nameof(corsPolicy.ExposedHeaders)) .SetValue(corsPolicy, CorsHelper.GetExposedHeaders()); app.UseCors(new CorsOptions { PolicyProvider = new CorsPolicyProvider { PolicyResolver = context => Task.FromResult(corsPolicy) } }); }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime applicationLifetime) { loggerFactory.AddConsole(); var logger = loggerFactory.CreateLogger <Startup>(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseDefaultFiles(); app.UseStaticFiles(); app.UseCors(builder => builder .AllowAnyHeader() .AllowAnyMethod() .AllowAnyOrigin() .WithExposedHeaders(CorsHelper.GetExposedHeaders())); app.Use(async(context, next) => { try { await next.Invoke(); } catch (Exception exc) { logger.LogError(null, exc, exc.Message); context.Response.StatusCode = 500; await context.Response.WriteAsync("An internal server error has occurred", context.RequestAborted); } }); app.UseTus(context => new DefaultTusConfiguration { UrlPath = "/files", Store = new TusDiskStore(@"C:\tusfiles\"), Events = new Events { OnBeforeCreateAsync = ctx => { // Partial files are not complete so we do not need to validate // the metadata in our example. if (ctx.FileConcatenation is FileConcatPartial) { return(Task.CompletedTask); } if (!ctx.Metadata.ContainsKey("name")) { ctx.FailRequest("name metadata must be specified. "); } if (!ctx.Metadata.ContainsKey("contentType")) { ctx.FailRequest("contentType metadata must be specified. "); } return(Task.CompletedTask); }, OnCreateCompleteAsync = ctx => { logger.LogInformation($"Created file {ctx.FileId} using {ctx.Store.GetType().FullName}"); return(Task.CompletedTask); }, OnBeforeDeleteAsync = ctx => { // Can the file be deleted? If not call ctx.FailRequest(<message>); return(Task.CompletedTask); }, OnDeleteCompleteAsync = ctx => { logger.LogInformation($"Deleted file {ctx.FileId} using {ctx.Store.GetType().FullName}"); return(Task.CompletedTask); }, OnFileCompleteAsync = ctx => { logger.LogInformation($"Upload of {ctx.FileId} completed using {ctx.Store.GetType().FullName}"); // If the store implements ITusReadableStore one could access the completed file here. // The default TusDiskStore implements this interface: //var file = await ((tusdotnet.Interfaces.ITusReadableStore)ctx.Store).GetFileAsync(ctx.FileId, ctx.CancellationToken); return(Task.CompletedTask); } }, // Set an expiration time where incomplete files can no longer be updated. // This value can either be absolute or sliding. // Absolute expiration will be saved per file on create // Sliding expiration will be saved per file on create and updated on each patch/update. Expiration = _absoluteExpiration }); app.Use(async(context, next) => { // All GET requests to tusdotnet are forwared so that you can handle file downloads. // This is done because the file's metadata is domain specific and thus cannot be handled // in a generic way by tusdotnet. if (!context.Request.Method.Equals("get", StringComparison.OrdinalIgnoreCase)) { await next.Invoke(); return; } var url = new Uri(context.Request.GetDisplayUrl()); if (url.LocalPath.StartsWith("/files/", StringComparison.Ordinal)) { var fileId = url.LocalPath.Replace("/files/", "").Trim(); if (!string.IsNullOrEmpty(fileId)) { var file = await _tusDiskStore.GetFileAsync(fileId, context.RequestAborted); if (file == null) { context.Response.StatusCode = 404; await context.Response.WriteAsync($"File with id {fileId} was not found.", context.RequestAborted); return; } var fileStream = await file.GetContentAsync(context.RequestAborted); var metadata = await file.GetMetadataAsync(context.RequestAborted); context.Response.ContentType = metadata.ContainsKey("contentType") ? metadata["contentType"].GetString(Encoding.UTF8) : "application/octet-stream"; if (metadata.TryGetValue("name", out var nameMeta)) { context.Response.Headers.Add("Content-Disposition", new[] { $"attachment; filename=\"{nameMeta.GetString(Encoding.UTF8)}\"" }); } await fileStream.CopyToAsync(context.Response.Body, 81920, context.RequestAborted); } } }); // Setup cleanup job to remove incomplete expired files. // This is just a simple example. In production one would use a cronjob/webjob and poll an endpoint that runs RemoveExpiredFilesAsync. var onAppDisposingToken = applicationLifetime.ApplicationStopping; Task.Run(async() => { while (!onAppDisposingToken.IsCancellationRequested) { logger.LogInformation("Running cleanup job..."); var numberOfRemovedFiles = await _tusDiskStore.RemoveExpiredFilesAsync(onAppDisposingToken); logger.LogInformation( $"Removed {numberOfRemovedFiles} expired files. Scheduled to run again in {_absoluteExpiration.Timeout.TotalMilliseconds} ms"); await Task.Delay(_absoluteExpiration.Timeout, onAppDisposingToken); } }, onAppDisposingToken); }
public void Configuration(IAppBuilder app) { var corsPolicy = new System.Web.Cors.CorsPolicy { AllowAnyHeader = true, AllowAnyMethod = true, AllowAnyOrigin = true }; // ReSharper disable once PossibleNullReferenceException - nameof will cause compiler error if the property does not exist. corsPolicy.GetType() .GetProperty(nameof(corsPolicy.ExposedHeaders)) .SetValue(corsPolicy, CorsHelper.GetExposedHeaders()); app.UseCors(new CorsOptions { PolicyProvider = new CorsPolicyProvider { PolicyResolver = context => Task.FromResult(corsPolicy) } }); app.Use(async(context, next) => { try { await next.Invoke(); } catch (Exception exc) { Console.Error.WriteLine(exc); } }); app.UseTus(request => { return(new DefaultTusConfiguration { Store = _tusDiskStore, UrlPath = "/files", OnUploadCompleteAsync = (fileId, store, cancellationToken) => { Console.WriteLine( $"Upload of {fileId} is complete. Callback also got a store of type {store.GetType().FullName}"); // If the store implements ITusReadableStore one could access the completed file here. // The default TusDiskStore implements this interface: // var file = await ((ITusReadableStore)store).GetFileAsync(fileId, cancellationToken); return Task.FromResult(true); }, // Set an expiration time where incomplete files can no longer be updated. // This value can either be absolute or sliding. // Absolute expiration will be saved per file on create // Sliding expiration will be saved per file on create and updated on each patch/update. Expiration = _absoluteExpiration }); }); app.Use(async(context, next) => { // All GET requests to tusdotnet are forwared so that you can handle file downloads. // This is done because the file's metadata is domain specific and thus cannot be handled // in a generic way by tusdotnet. if (!context.Request.Method.Equals("get", StringComparison.InvariantCultureIgnoreCase)) { await next.Invoke(); return; } if (context.Request.Uri.LocalPath.StartsWith("/files/", StringComparison.Ordinal)) { var fileId = context.Request.Uri.LocalPath.Replace("/files/", "").Trim(); if (!string.IsNullOrEmpty(fileId)) { var file = await _tusDiskStore.GetFileAsync(fileId, context.Request.CallCancelled); if (file == null) { context.Response.StatusCode = 404; await context.Response.WriteAsync($"File with id {fileId} was not found.", context.Request.CallCancelled); return; } var fileStream = await file.GetContentAsync(context.Request.CallCancelled); var metadata = await file.GetMetadataAsync(context.Request.CallCancelled); context.Response.ContentType = metadata.ContainsKey("contentType") ? metadata["contentType"].GetString(Encoding.UTF8) : "application/octet-stream"; if (metadata.ContainsKey("name")) { var name = metadata["name"].GetString(Encoding.UTF8); context.Response.Headers.Add("Content-Disposition", new[] { $"attachment; filename=\"{name}\"" }); } await fileStream.CopyToAsync(context.Response.Body, 81920, context.Request.CallCancelled); return; } } switch (context.Request.Uri.LocalPath) { case "/": context.Response.ContentType = "text/html"; await context.Response.WriteAsync(File.ReadAllText("../../index.html"), context.Request.CallCancelled); break; case "/tus.js": context.Response.ContentType = "application/js"; await context.Response.WriteAsync(File.ReadAllText("../../tus.js"), context.Request.CallCancelled); break; default: context.Response.StatusCode = 404; break; } }); // Setup cleanup job to remove incomplete expired files. // This is just a simple example. In production one would use a cronjob/webjob and poll an endpoint that runs RemoveExpiredFilesAsync. var onAppDisposingToken = new OwinContext(app.Properties).Get <CancellationToken>("host.OnAppDisposing"); Task.Run(async() => { while (!onAppDisposingToken.IsCancellationRequested) { Console.WriteLine("Running cleanup job..."); var numberOfRemovedFiles = await _tusDiskStore.RemoveExpiredFilesAsync(onAppDisposingToken); Console.WriteLine( $"Removed {numberOfRemovedFiles} expired files. Scheduled to run again in {_absoluteExpiration.Timeout.TotalMilliseconds} ms"); await Task.Delay(_absoluteExpiration.Timeout, onAppDisposingToken); } }, onAppDisposingToken); }