使用分块技术实现向ASP.Net Web API的多个异步文件上传

10
我已经阅读了许多相关问题,但没有一个完全符合这个问题。如果是重复的,请给我发送链接。
我正在使用流js库的angular版本进行HTML5文件上传 (https://github.com/flowjs/ng-flow)。这个库非常好用,我能够同时上传多个文件,每个文件以1MB为单位。有一个ASP.Net Web API文件控制器接受这些文件并将它们保存到磁盘上。虽然我可以使其工作,但我不高效,想知道更好的方法。
首先,我在异步方法中使用MultipartFormDataStreamProvider,只要文件在单个块中上传,就可以很好地工作。然后我切换到只使用FileStream将文件写入磁盘。只要块按顺序到达,这也可以工作,但当然,我不能依赖于它们的顺序。
接下来,只是为了看到它的工作原理,我将块写入单独的文件流中,并在上传完成后将它们组合起来,因此效率低下。一个1GB的文件会生成一千个需要在上传完成后读取和重新写入的块。我可以将所有文件块保存在内存中,并在全部上传后将它们刷新,但我担心服务器会崩溃。
似乎应该有一个不错的异步解决方案来解决这个问题,但我不知道是什么。一种可能性是在编写当前块时使用async/await来组合先前的块。另一种可能是使用Begin/EndInvoke创建一个单独的线程,以便磁盘上的文件操作独立于读取HttpContext的线程处理,但这将依赖于ThreadPool,我担心当我的MVC控制器返回时,创建的线程会被过度终止。我可以创建一个完全独立于ASP.Net的FileWatcher,但那会非常麻烦。

所以我的问题是,1)我是否错过了已经存在的简单解决方案?(似乎应该有)2)如果没有,什么是在Web API框架内解决这个问题的最佳方法?

谢谢,Bob

3个回答

10

我不熟悉那种分块上传技术,但我相信以下方式可行:

  • 使用flowTotalSize预先分配内存空间(pre-allocate the file),在第一个块数据到来时。
  • 针对每个文件使用一个SemaphoreSlim 来对该文件的异步写入进行串行化。
  • 每个块数据将会写入文件中自己的偏移量(write to its own offset),即flowChunkSize * (flowChunkNumber - 1)

这种方法不能处理意外终止上传的情况。解决这种问题通常需要分配/写入临时文件(文件名带有特殊扩展名),然后在最后一个块到达后移动/重命名该文件。

别忘了确保您的文件写入实际上是异步的


听起来太完美了!我不知道那可以这样做。希望今天下午我能回到这段代码并尝试一下。谢谢! - bob
我尝试过这个,但似乎你不能无序地写入块而不抛出异常。Jon Skeet在这个问题中也是这么说的:http://tinyurl.com/kelqk9k。 - bob
无法插入。我建议的是覆盖。这应该可以工作。 - Stephen Cleary
抱歉,我忘记回来将其标记为正确答案。当我正确实现时,它的效果非常好。 - bob
3
@Bob - 我也想做同样的事情。你成功的解决方案是否公开可用?我需要一些示例代码来帮助我入手。 - Herb Caudill

7
使用@Stephen Cleary的答案以及这个线程:https://github.com/flowjs/ng-flow/issues/41,我成功地创建了一个ASP.NET Web Api实现,并上传到了GitHub供像@Herb Caudill这样的人参考。

https://github.com/samhowes/NgFlowSample/tree/master

原始答案是这个问题的真正答案,但我还没有足够的声望来发表评论。我没有使用SemaphoreSlim,而是启用了文件写共享。但是,我确实预先分配并确保每个块都被写入正确的位置,通过计算偏移量。

我将把这个示例贡献给Flow样本:https://github.com/flowjs/flow.js/tree/master/samples


值得一提的是,“我还没有足够的声望”不是一个有效的理由。仅链接回答也是不鼓励的。但是感谢您的帮助;我们的规则需要一段时间来适应。 - Veedrac

0

这是我所做的。上传块并将这些块保存在服务器上,并将块的位置与其顺序(不是它们进入的顺序,而是文件中块的顺序)保存在数据库中。

然后,我引入了另一个端点来合并这些块。由于这部分可能是一个长时间的过程,我使用了消息服务在后台运行该过程。

当服务完成文件合并后,发送通知(或者您可以触发事件)。

同意,这并不能解决保存所有这些块的问题,但是在合并完成后,我们可以从磁盘中删除它们。但是,为了使上传顺利工作,需要进行一些IIS配置。

这是我对这个老问题的看法。现在大多数应用程序都使用Azure或AWS进行存储。但是,如果有帮助的话,我仍然分享我的想法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接