如何在没有临时文件的情况下将流从Web API传递到Azure Blob存储?

2ul0zpep  于 2022-11-25  发布在  其他
关注(0)|答案(2)|浏览(148)

我正在开发一个应用程序,其中文件上传经常发生,并且可以相当大的大小。
这些文件将被上传到Web API,然后Web API将从请求中获取流,并将其传递到我的存储服务,然后存储服务将其上传到Azure Blob存储。
我需要确保:

  • 未在Web API示例上写入任何临时文件
  • 在将请求流传递给存储服务之前,不会将其完全读入内存(以防止OutOfMemoryExceptions)。

我看过this article,它描述了如何禁用输入流缓冲,但是因为来自许多不同用户的许多文件上传同时发生,所以它实际上做到它在tin上所说的是很重要的。
这是我目前控制器中的内容:

if (this.Request.Content.IsMimeMultipartContent())
{
    var provider = new MultipartMemoryStreamProvider();
    await this.Request.Content.ReadAsMultipartAsync(provider);
    var fileContent = provider.Contents.SingleOrDefault();

    if (fileContent == null)
    {
        throw new ArgumentException("No filename.");
    }

    var fileName = fileContent.Headers.ContentDisposition.FileName.Replace("\"", string.Empty);
    
    // I need to make sure this stream is ready to be processed by 
    // the Azure client lib, but not buffered fully, to prevent OoM.
    var stream = await fileContent.ReadAsStreamAsync();
}

我不知道如何才能可靠地测试这个。

EDIT:我忘了提到直接上传到Blob存储(绕过我的API)不起作用,因为我正在做一些大小检查(例如,此用户可以上传500mb吗?此用户是否已使用其配额?)。

ddhy6vgd

ddhy6vgd1#

解决了它,在这个Gist的帮助下。
下面是我如何使用它,沿着一个聪明的“黑客”,以获得实际的文件大小,而不是复制文件到内存第一。哦,它的速度是两倍(显然)。

// Create an instance of our provider.
// See https://gist.github.com/JamesRandall/11088079#file-blobstoragemultipartstreamprovider-cs for implementation.
var provider = new BlobStorageMultipartStreamProvider ();

// This is where the uploading is happening, by writing to the Azure stream
// as the file stream from the request is being read, leaving almost no memory footprint.
await this.Request.Content.ReadAsMultipartAsync(provider);

// We want to know the exact size of the file, but this info is not available to us before
// we've uploaded everything - which has just happened.
// We get the stream from the content (and that stream is the same instance we wrote to).
var stream = await provider.Contents.First().ReadAsStreamAsync();

// Problem: If you try to use stream.Length, you'll get an exception, because BlobWriteStream
// does not support it.

// But this is where we get fancy.

// Position == size, because the file has just been written to it, leaving the
// position at the end of the file.
var sizeInBytes = stream.Position;

瞧,你得到了你上传的文件的大小,而不必复制文件到您的web示例的内存.
至于在上传文件之前**获得文件长度,这并不容易,我不得不求助于一些相当不愉快的方法来获得一个近似值。
BlobStorageMultipartStreamProvider中:

var approxSize = parent.Headers.ContentLength.Value - parent.Headers.ToString().Length;

这给了我一个非常接近的文件大小,减少了几百个字节(我猜取决于HTTP头)。这对我来说已经足够好了,因为我的配额强制可以接受被削减的几个字节。
为了炫耀一下,这里是内存占用量,由任务管理器中的“性能”选项卡报告。

Before -使用MemoryStream,在上传之前将其读入内存

之后-直接写入Blob存储

dldeef67

dldeef672#

我认为更好的方法是从客户端直接访问Azure Blob存储。通过利用Azure存储中的CORS支持,您可以消除Web API服务器上的负载,从而为您的应用程序提供更好的整体可伸缩性。
基本上,你将创建一个共享访问签名(SAS)URL,你的客户端可以使用该URL将文件直接上载到Azure存储。出于安全原因,建议你限制SAS的有效期。有关生成SAS URL的最佳做法指南,请参阅here
对于您的特定场景,请查看Azure存储团队的this blog,他们讨论了如何使用CORS和SAS来实现此特定场景。此外,还有一个示例应用程序,因此它应该可以为您提供所需的一切。

相关问题