You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在tusdotnet中针对每次上传请求动态覆盖存储路径?

解决tusdotnet动态设置单请求存储路径的问题

tusdotnet默认在启动时全局配置存储实例,直接在OnBeforeCreateAsync等事件中替换ctx.Store不会生效——因为框架内部会复用全局初始化的存储实例,事件上下文的Store只是引用,无法覆盖全局配置。要实现按请求动态设置存储路径,需要通过自定义ITusStore实现来适配。

解决方案:自定义动态存储类

创建一个包装类,通过IHttpContextAccessor获取当前请求上下文,解析Query参数生成对应路径,动态创建TusDiskStore处理当前请求的存储操作。

1. 实现自定义动态存储类

public class DynamicTusDiskStore : ITusStore, ITusCreationStore, ITusReadableStore, ITusDeletableStore
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    // 可选:缓存已创建的TusDiskStore实例,减少重复创建开销
    private readonly ConcurrentDictionary<string, TusDiskStore> _storeCache = new();

    public DynamicTusDiskStore(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    private TusDiskStore GetCurrentStore()
    {
        var httpContext = _httpContextAccessor.HttpContext ?? 
            throw new InvalidOperationException("HttpContext不可用");

        // 从Query参数解析目标路径(替换为你的GetUploadStoreDir实现)
        var uploadDir = GetUploadStoreDir(httpContext.Request.Query);
        var fullPath = Path.GetFullPath(uploadDir);
        
        // 创建目录(确保路径合法,避免遍历攻击)
        if (!Directory.Exists(fullPath))
            Directory.CreateDirectory(fullPath);

        // 从缓存获取或创建TusDiskStore实例
        return _storeCache.GetOrAdd(fullPath, path => new TusDiskStore(path));
    }

    // 所有存储接口方法委托给动态生成的TusDiskStore实例
    public Task<ITusFile> GetFileAsync(string fileId, CancellationToken cancellationToken)
        => GetCurrentStore().GetFileAsync(fileId, cancellationToken);

    public Task DeleteFileAsync(string fileId, CancellationToken cancellationToken)
        => GetCurrentStore().DeleteFileAsync(fileId, cancellationToken);

    public Task<long> AppendDataAsync(string fileId, Stream stream, CancellationToken cancellationToken)
        => GetCurrentStore().AppendDataAsync(fileId, stream, cancellationToken);

    public Task<string> CreateFileAsync(long uploadLength, string metadata, CancellationToken cancellationToken)
        => ((ITusCreationStore)GetCurrentStore()).CreateFileAsync(uploadLength, metadata, cancellationToken);

    public Task<long> GetUploadLengthAsync(string fileId, CancellationToken cancellationToken)
        => GetCurrentStore().GetUploadLengthAsync(fileId, cancellationToken);

    public Task<long> GetUploadOffsetAsync(string fileId, CancellationToken cancellationToken)
        => GetCurrentStore().GetUploadOffsetAsync(fileId, cancellationToken);

    public Task SetUploadLengthAsync(string fileId, long uploadLength, CancellationToken cancellationToken)
        => GetCurrentStore().SetUploadLengthAsync(fileId, uploadLength, cancellationToken);

    // 若需支持过期、校验等其他接口,同理添加委托实现
}

2. 注册服务并配置tusdotnet

在Startup.cs中注册IHttpContextAccessor,并使用自定义存储类:

public void ConfigureServices(IServiceCollection services)
{
    // 注册HttpContext访问器
    services.AddHttpContextAccessor();
    // 其他服务注册...
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 其他中间件配置...

    app.UseTus(context => new DefaultTusConfiguration
    {
        UrlPath = "/files",
        // 使用自定义动态存储类
        Store = new DynamicTusDiskStore(context.RequestServices.GetRequiredService<IHttpContextAccessor>()),
        Events = new Events
        {
            OnFileCompleteAsync = async eventContext =>
            {
                // 从当前请求上下文重新解析路径,获取对应存储实例
                var httpContext = eventContext.HttpContext;
                var uploadDir = GetUploadStoreDir(httpContext.Request.Query);
                var fullPath = Path.GetFullPath(uploadDir);
                var store = new TusDiskStore(fullPath);

                ITusFile file = await store.GetFileAsync(eventContext.FileId, CancellationToken.None);
                var result = await BuildFilePlacementAsync(file, store);

                if (!result.Success)
                {
                    throw new MyProcessingException("文件处理失败");
                }
            }
        }
    });
}

关键注意事项

  • 路径安全:必须对Query参数中的路径进行合法性校验,防止路径遍历攻击(比如限制路径只能在指定根目录下)。
  • 缓存策略:使用ConcurrentDictionary缓存TusDiskStore实例时,需考虑定期清理长期未使用的实例,避免内存泄漏。
  • 接口覆盖:根据你的业务需求,实现ITusStore的所有必要接口(如ITusExpirationStore用于过期文件清理)。

内容的提问来源于stack exchange,提问作者Zap

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 02:54:55