大文件上传导致文件损坏

Large File Upload Results in Corrupted Files

提问人:Saqif Haque 提问时间:10/28/2023 最后编辑:Saqif Haque 更新时间:10/29/2023 访问量:57

问:

我目前正在我的应用程序中开发文件上传功能,并且我遇到了上传的文件似乎已损坏的问题。该应用程序旨在处理块中的大文件上传,虽然它成功收集了所有具有匹配大小的块,但最终组装的文件似乎已损坏,无法打开。

我已经实现了一个内存流来处理文件块,组装的文件在收到最终块时保存到磁盘中。尽管如此,生成的文件仍然无法访问。我怀疑内存流的使用方式或对磁盘的最终写入操作可能存在问题。

我正在使用以下代码来管理文件块并保存组装的文件:

private readonly ConcurrentDictionary<string, MemoryStream> fileChunks = new ConcurrentDictionary<string, MemoryStream>();
[HttpPost("UploadFileChunk")]
public async Task<bool> UploadFileChunk([FromBody] FileChunkDto fileChunkDto)
{
    try
    {
        // get the local filename
        string filePath = Environment.CurrentDirectory + "\\StaticFiles\\";
        string fileName = filePath + fileChunkDto.FileName;

        // delete the file if necessary
        //if (fileChunkDto.FirstChunk && System.IO.File.Exists(fileName))
        //    System.IO.File.Delete(fileName);

        if (!fileChunks.TryGetValue(fileName, out MemoryStream? memoryStream))
        {
            memoryStream = new MemoryStream();
            fileChunks.TryAdd(fileName, memoryStream);
        }

        memoryStream.Seek(fileChunkDto.Offset, SeekOrigin.Begin);
        await memoryStream.WriteAsync(fileChunkDto.Data!, 0, fileChunkDto.Data!.Length);

        if (fileChunkDto.LastChunk)
        {
            using (FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write))
            {
                memoryStream.Seek(0, SeekOrigin.Begin);
                await memoryStream.CopyToAsync(fileStream);
            }

            memoryStream.Dispose();

            var extention = Path.GetExtension(fileChunkDto.FileName).ToLower();
            var justFileName = Path.GetFileNameWithoutExtension(fileChunkDto.FileName);
            var (fileName2, fileType) = GetFileNameFileType(fileChunkDto.To!, justFileName);


            var to = fileChunkDto.To!.ToLower();

            var clientsRequest = new ClientsRequest()
            {
                ClientId = fileChunkDto.ClientId,
                RequestId = fileChunkDto.RequestId,
                CreateDate = DateTime.UtcNow

            };

            await _context.ClientsRequests.AddAsync(clientsRequest);
            await _context.SaveChangesAsync();

            string filenameAfterConvertion = await UploadDocumentToS3(fileChunkDto, fileName, fileType);

            if (filenameAfterConvertion != null)
            {
                return true;
            }

            return false;
        }

        return false;

    }
    catch (Exception ex)
    {
        var msg = ex.Message;
        return false;

    }


} 

我怀疑我管理内存流和处理最终写入操作的方式可能存在问题,但我似乎无法弄清楚

我将非常感谢有关如何解决此问题并确保上传的文件未损坏的任何见解或建议。提前感谢您的帮助,如果需要更多信息,请在评论中告诉我。

C# asp.net 上传 大文件 内存流

评论

0赞 Selvin 10/28/2023
memoryStream.Dispose() 然后从字典中 rused ....
0赞 Selvin 10/28/2023
还有内存流的意义是什么,你可以从中列出文件块和汇编文件......内存方面会更好
0赞 Saqif Haque 10/28/2023
你能详细说明一下吗?
0赞 Selvin 10/28/2023
你能详细说明一下你不明白的地方吗?你已经有 filechunks。数据。。。MemoryStream 在引擎盖下使用字节数组...所以你是在浪费内存
0赞 Saqif Haque 10/28/2023
问题不在于性能。感谢您指出这一点。但是为什么文件会损坏呢?我删除了内存流,并将文件块存储在字节数组列表中。现在可以打开文件,但内容仍然损坏。

答:

1赞 Akash Kava 10/29/2023 #1

错误在线

memoryStream.Seek(fileChunkDto.Offset, SeekOrigin.Begin);
await memoryStream.WriteAsync(fileChunkDto.Data!, 0, fileChunkDto.Data!.Length);

尝试为同一文件上传块的两个同时请求将损坏内存流,因为您没有对内存流进行任何锁定。

这种方法也是不正确的,您正在传输所有块并将其保存在内存中。理想情况下,您应该将块保存在文件中,并将一个文件保存为服务器上的小型独立文件,这样它就不会损坏。每个文件都应根据客户端请求具有唯一的名称。最后,您可以按块偏移量的升序将所有文件附加到 S3。

S3 已经支持分段上传,您可以利用 S3 自己的上传 API 来执行您正在尝试执行的操作。