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

ASP.NET+Dropzone分片上传异常:文件空白、文件名含下划线且体积偏大

ASP.NET Dropzone分片上传图片损坏问题排查与解决

问题描述

在ASP.NET环境中使用Dropzone实现图片分片上传功能,点击提交按钮后出现以下异常:

  • 上传后的图片为空白损坏状态
  • 生成的文件带有下划线后缀(未合并为原文件名)
  • 文件体积远大于原文件

已排查前端ASPX代码及后端ChunkedUploadHandler.ashx代码,但未定位问题,以下是具体问题成因分析与解决方法:


核心问题成因

  1. 分片存储逻辑错误:所有分片被追加写入同一个临时文件,导致分片数据叠加混乱;合并时又重复读取该文件多次,最终文件是重复数据拼接,直接造成损坏和体积异常。
  2. HTTP请求未区分处理:前端在chunksUploaded中发送PUT请求触发合并,但后端未针对PUT方法做单独逻辑处理,仍执行分片写入操作,导致额外写入无效数据。
  3. 参数获取方式错误:后端仅从QueryString读取参数,但Dropzone的分片参数(如dzChunkIndex)实际存在于POST的FormData中,导致部分参数读取异常,合并触发条件失效。
  4. 合并阶段文件操作错误:合并循环中反复读取并删除同一个临时文件,第二次循环时文件已被删除,引发异常且无法完成正确合并。

解决方法

1. 修正后端分片存储逻辑

修改ProcessRequest方法,为每个分片生成带索引的独立临时文件,避免分片数据混淆:

public void ProcessRequest(HttpContext context)
{
    context.Response.ContentType = "text/plain";
    string eventId = context.Request["eventID"];
    string dzUuid = context.Request["dzUuid"];
    string dzFilename = context.Request["dzFilename"];

    switch (context.Request.HttpMethod.ToUpper())
    {
        case "POST":
            HandleChunkUpload(context);
            break;
        case "PUT":
            int totalChunks = int.Parse(context.Request["dzTotalChunkCount"]);
            CombineChunks(HttpContext.Current.Server.MapPath($"~/Events/originals/{eventId}"), dzUuid, dzFilename, totalChunks);
            context.Response.Write("合并完成");
            break;
        case "DELETE":
            DeleteTempFiles(context);
            break;
        default:
            context.Response.StatusCode = 405;
            context.Response.Write("不支持的请求方法");
            break;
    }
}

private void HandleChunkUpload(HttpContext context)
{
    string dzUuid = context.Request["dzUuid"];
    string dzFilename = context.Request["dzFilename"];
    string dzTotalChunkCount = context.Request["dzTotalChunkCount"];
    string eventId = context.Request["eventID"];
    int currentChunkIndex = int.TryParse(context.Request["dzChunkIndex"], out int idx) ? idx : -1;

    string chunkDirectory = HttpContext.Current.Server.MapPath($"~/Events/originals/{eventId}");
    if (!Directory.Exists(chunkDirectory))
    {
        Directory.CreateDirectory(chunkDirectory);
    }

    // 为每个分片生成带索引的唯一文件名
    string chunkFilePath = Path.Combine(chunkDirectory, $"{dzUuid}_{currentChunkIndex}_{dzFilename}");

    using (FileStream fs = new FileStream(chunkFilePath, FileMode.Create))
    {
        byte[] buffer = new byte[context.Request.InputStream.Length];
        context.Request.InputStream.Read(buffer, 0, buffer.Length);
        fs.Write(buffer, 0, buffer.Length);
    }

    // 检查是否所有分片上传完成
    if (currentChunkIndex != -1 && !string.IsNullOrEmpty(dzTotalChunkCount) 
        && currentChunkIndex + 1 == int.Parse(dzTotalChunkCount))
    {
        CombineChunks(chunkDirectory, dzUuid, dzFilename, int.Parse(dzTotalChunkCount));
    }
}

2. 修正分片合并逻辑

修改CombineChunks方法,按分片索引依次读取并合并,完成后删除所有临时分片文件:

private void CombineChunks(string chunkDirectory, string dzUuid, string fileName, int totalChunks)
{
    string finalFilePath = Path.Combine(chunkDirectory, fileName);

    using (FileStream finalFileStream = new FileStream(finalFilePath, FileMode.Create))
    {
        for (int i = 0; i < totalChunks; i++)
        {
            string chunkFilePath = Path.Combine(chunkDirectory, $"{dzUuid}_{i}_{fileName}");
            if (File.Exists(chunkFilePath))
            {
                byte[] chunkData = File.ReadAllBytes(chunkFilePath);
                finalFileStream.Write(chunkData, 0, chunkData.Length);
                File.Delete(chunkFilePath);
            }
        }
    }
}

3. 新增临时文件删除逻辑

实现DeleteTempFiles方法,处理上传失败时的临时文件清理:

private void DeleteTempFiles(HttpContext context)
{
    string dzIdentifier = context.Request["dzIdentifier"];
    string fileName = context.Request["fileName"];
    string totalChunks = context.Request["totalChunks"];
    string eventId = context.Request["eventID"];

    string chunkDirectory = HttpContext.Current.Server.MapPath($"~/Events/originals/{eventId}");
    if (Directory.Exists(chunkDirectory))
    {
        for (int i = 0; i < int.Parse(totalChunks); i++)
        {
            string tempFilePath = Path.Combine(chunkDirectory, $"{dzIdentifier}_{i}_{fileName}");
            if (File.Exists(tempFilePath))
            {
                File.Delete(tempFilePath);
            }
        }
    }
    context.Response.Write("临时文件已删除");
}

4. 前端可选优化

若开启了parallelChunkUploads: true,后端已通过分片索引区分文件,无需修改;若担心并发写入问题,可暂时设置为parallelChunkUploads: false,确保分片按顺序上传。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 08:45:08