C# MVC Download Big File from S3 Async

后端 未结 2 705
失恋的感觉
失恋的感觉 2021-02-09 01:13

I have to download a file from aws S3 async. I have a anchor tag, on clicking it a method will be hit in a controller for download. The file should be start downloading at the b

相关标签:
2条回答
  • 2021-02-09 01:56

    You must send ContentLength to client in order to display a progress. Browser has no information about how much data it will receive.

    If you look at source of FileStreamResult class, used by File method, it does not inform client about "Content-Length". https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/FileStreamResult.cs

    Replace this,

    return File(response, mimeType, downLoadName);
    

    with

    return new FileStreamResultEx(response, res.ContentLength, mimeType, downloadName);
    
    
    public class FileStreamResultEx : ActionResult{
    
         public FileStreamResultEx(
            Stream stream, 
            long contentLength,         
            string mimeType,
            string fileName){
            this.stream = stream;
            this.mimeType = mimeType;
            this.fileName = fileName;
            this.contentLength = contentLength;
         }
    
    
         public override void ExecuteResult(
             ControllerContext context)
         {
             var response = context.HttpContext.Response; 
             response.BufferOutput = false;
             response.Headers.Add("Content-Type", mimeType);
             response.Headers.Add("Content-Length", contentLength.ToString());
             response.Headers.Add("Content-Disposition","attachment; filename=" + fileName);
    
             using(stream) { 
                 stream.CopyTo(response.OutputStream);
             }
         }
    
    }
    

    Alternative

    Generally this is a bad practice to download and deliver S3 file from your server. You will be charged twice bandwidth on your hosting account. Instead, you can use signed URLs to deliver non public S3 objects, with few seconds of time to live. You could simply use Pre-Signed-URL

     public ActionResult Action(){
         try{
             using(AmazonS3Client client = 
                  new AmazonS3Client(accessKeyID, secretAccessKey)){
                var bucketName = 
                     ConfigurationManager.AppSettings["bucketName"]
                    .ToString() + DownloadPath;
                GetPreSignedUrlRequest request1 = 
                   new GetPreSignedUrlRequest(){
                      BucketName = bucketName,
                      Key = originalName,
                      Expires = DateTime.Now.AddMinutes(5)
                   };
    
                string url = client.GetPreSignedURL(request1);
                return Redirect(url);
             }
         }
         catch (Exception)
         {
             failure = "File download failed. Please try after some time.";   
         }              
     }
    

    As long as object do not have public read policy, objects are not accessible to users without signing.

    Also, you must use using around AmazonS3Client in order to quickly dispose networks resources, or just use one static instance of AmazonS3Client that will reduce unnecessary allocation and deallocation.

    0 讨论(0)
  • 2021-02-09 02:16

    As i understand, you want to make something like "reverse proxy" from your server to S3. Very userful article how to do that with Nginx you can find here: https://stackoverflow.com/a/44749584

    0 讨论(0)
提交回复
热议问题