有没有更快的方法通过 Web Api 将对象从 S3 流式传输到我的 Angular 客户端而不是使用它们的响应流?
Is there a faster way to stream objects from S3 over Web Api to my Angular client instead of using their response stream?
为了实现安全文件下载,我的 Angular 客户端请求一个一次性令牌,然后浏览器使用令牌访问此端点以启动从 Asp.Net Web 下载 Api .然后服务器从亚马逊检索对象,我将他们的响应流复制到 MemoryStream,如下所示:
using (var client = new AmazonS3Client())
{
var request = new GetObjectRequest
{
BucketName = bucket,
Key = key
};
using (var response = await client.GetObjectAsync(request))
using (var responseStream = response.ResponseStream)
{
streamResponse.ContentLength = response.ContentLength;
await responseStream.CopyToAsync(streamResponse.Stream);
streamResponse.Stream.Position = 0;
return streamResponse;
}
}
一旦这个包装器对象 (streamResponse) 返回到控制器,我 return 一个新的 HttpResponse,其 Content 设置为一个新的 StreamContent,从之前的方法接收 MemoryStream:
var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(_objectFromStorageResponse.StreamResponse.Stream)
};
httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = _objectFromStorageResponse.FileNm
};
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
httpResponseMessage.Content.Headers.ContentLength = _objectFromStorageResponse.StreamResponse.ContentLength;
return httpResponseMessage;
现在,就 UI -> Api 下载而言,这实际上非常有效。文件流式传输到浏览器就好了。
然而,对于大文件 (>250MB),复制流部分变得非常慢。 所以 UI 必须挂起一点,造成一个糟糕的等待下载开始时的用户体验。显然,我可以通过显示消息或微调器让用户知道我们正在处理下载来缓解 UI 问题。但我忍不住认为有更好的方法可以让最终用户更快地开始下载过程。我试过直接 returning 流而不是将流复制到我自己的流,但我认为无论如何控制器都会自己做,因为体验似乎是一样的。
这是我从我的网络 api 中利用流式 S3 对象的唯一方法吗?这样做有另一种模式吗?或者这就是它的工作原理,我需要向用户显示一些反馈?
如何使用 Presigned URL 的组合并使用 HTTP 303/307 状态进行响应。
例如,您需要将对象 x 共享给客户端。
- 为 S3 存储桶中的 blob/object 生成预签名 URL 2 分钟(假设下载内容需要多长时间)。
- 响应 HTTP 303(或 307)状态和预签名 URL。
- browser/client app/or curl 命令将跟随 HTTP 307 直接使用预签名 URL.
重定向到 S3 存储桶
即使某些恶意玩家获得预签名 URL,它也只有 2 分钟的有效期。
这样,您的服务器端代码就不必在传输字节时扮演中间人的角色。此方法会释放服务器上的大量内存和时间来处理其他 API 请求。
为了实现安全文件下载,我的 Angular 客户端请求一个一次性令牌,然后浏览器使用令牌访问此端点以启动从 Asp.Net Web 下载 Api .然后服务器从亚马逊检索对象,我将他们的响应流复制到 MemoryStream,如下所示:
using (var client = new AmazonS3Client())
{
var request = new GetObjectRequest
{
BucketName = bucket,
Key = key
};
using (var response = await client.GetObjectAsync(request))
using (var responseStream = response.ResponseStream)
{
streamResponse.ContentLength = response.ContentLength;
await responseStream.CopyToAsync(streamResponse.Stream);
streamResponse.Stream.Position = 0;
return streamResponse;
}
}
一旦这个包装器对象 (streamResponse) 返回到控制器,我 return 一个新的 HttpResponse,其 Content 设置为一个新的 StreamContent,从之前的方法接收 MemoryStream:
var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(_objectFromStorageResponse.StreamResponse.Stream)
};
httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = _objectFromStorageResponse.FileNm
};
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
httpResponseMessage.Content.Headers.ContentLength = _objectFromStorageResponse.StreamResponse.ContentLength;
return httpResponseMessage;
现在,就 UI -> Api 下载而言,这实际上非常有效。文件流式传输到浏览器就好了。
然而,对于大文件 (>250MB),复制流部分变得非常慢。 所以 UI 必须挂起一点,造成一个糟糕的等待下载开始时的用户体验。显然,我可以通过显示消息或微调器让用户知道我们正在处理下载来缓解 UI 问题。但我忍不住认为有更好的方法可以让最终用户更快地开始下载过程。我试过直接 returning 流而不是将流复制到我自己的流,但我认为无论如何控制器都会自己做,因为体验似乎是一样的。
这是我从我的网络 api 中利用流式 S3 对象的唯一方法吗?这样做有另一种模式吗?或者这就是它的工作原理,我需要向用户显示一些反馈?
如何使用 Presigned URL 的组合并使用 HTTP 303/307 状态进行响应。
例如,您需要将对象 x 共享给客户端。
- 为 S3 存储桶中的 blob/object 生成预签名 URL 2 分钟(假设下载内容需要多长时间)。
- 响应 HTTP 303(或 307)状态和预签名 URL。
- browser/client app/or curl 命令将跟随 HTTP 307 直接使用预签名 URL. 重定向到 S3 存储桶
即使某些恶意玩家获得预签名 URL,它也只有 2 分钟的有效期。
这样,您的服务器端代码就不必在传输字节时扮演中间人的角色。此方法会释放服务器上的大量内存和时间来处理其他 API 请求。