1

I have a Web Api controller method that gets passed document IDs and it should return the document files individually for those requested Ids. I have tried the accepted answer from the following link to achieve this functionality, but it's not working. I don't know where I did go wrong.

What's the best way to serve up multiple binary files from a single WebApi method?

My Web Api Method,

   public async Task<HttpResponseMessage> DownloadMultiDocumentAsync( 
             IClaimedUser user, string documentId)
    {
        List<long> docIds = documentId.Split(',').Select(long.Parse).ToList();
        List<Document> documentList = coreDataContext.Documents.Where(d => docIds.Contains(d.DocumentId) && d.IsActive).ToList();

        var content = new MultipartContent();
        CloudBlockBlob blob = null;

        var container = GetBlobClient(tenantInfo);
        var directory = container.GetDirectoryReference(
            string.Format(DirectoryNameConfigValue, tenantInfo.TenantId.ToString(), documentList[0].ProjectId));

        for (int docId = 0; docId < documentList.Count; docId++)
        {
            blob = directory.GetBlockBlobReference(DocumentNameConfigValue + documentList[docId].DocumentId);
            if (!blob.Exists()) continue;

            MemoryStream memStream = new MemoryStream();
            await blob.DownloadToStreamAsync(memStream);
            memStream.Seek(0, SeekOrigin.Begin);
            var streamContent = new StreamContent(memStream);
            content.Add(streamContent);

        }            
        HttpResponseMessage httpResponseMessage = new HttpResponseMessage();
        httpResponseMessage.Content = content;
        httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
        httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
        httpResponseMessage.StatusCode = HttpStatusCode.OK;
        return httpResponseMessage;
    }

I tried with 2 or more document Ids but only one file was downloaded and that also is not in the correct format (without extension).

Prabodh Mhalgi
  • 805
  • 9
  • 25
Md Aslam
  • 1,228
  • 8
  • 27
  • 61
  • I know it's going to be 2 years. Did you figure out a solution for this? I'm running into same issue. Can you please share your findings? – user2782405 Apr 20 '20 at 06:59

2 Answers2

5

Zipping is the only option that will have consistent result on all browsers. MIME/multipart content is for email messages (https://en.wikipedia.org/wiki/MIME#Multipart_messages) and it was never intended to be received and parsed on the client side of a HTTP transaction. Some browsers do implement it, some others don't.

Alternatively, you can change your API to take in a single docId and iterate over your API from your client for each docId.

Mehdi Ibrahim
  • 2,434
  • 1
  • 13
  • 13
  • @ Mehdi, I have tried the zipping option its working fine but the requirement here is separately it should download, so Is it possible? – Md Aslam Aug 25 '18 at 04:42
  • 1
    I tried to explain that mime/multipart is not implemented consistently across browsers and there is no guarantee it will consistently work in future releases of the same browser. e.g. your solution may work in Firefox but not Chrome; and that too, there is no guarantee it will continue to work in future versions of Firefox. The alternative I would suggest is the latter part of my answer - can you change your API to take in a single docId and iterate over your API from your client for each docId? – Mehdi Ibrahim Aug 25 '18 at 04:46
  • I have a method to download a single document also. Now users' requirement is multiple document download separately, that's why I am doing R&D for this. Anyway, Thank you so much for your explanation but If you can provide some sample code for this multi doc download (No Problem even it will work in one browser only), help me with this. – Md Aslam Aug 25 '18 at 04:52
  • Not sure why you feel calling the API one at a time for each docId in a loop would produce a different end result. This will give you the multiple document download functionality - in a cross browser compatible way. In http, one request can only have one response. Is the client a javascript/web app? Also did you try your own solution with Firefox? From my understanding, Firefox was the only browser to have supported it but that support broke in 2013 ("As of Firefox version 22, Mozilla has removed support for multipart mixed/replace" https://bugs.launchpad.net/evergreen/+bug/1198983). – Mehdi Ibrahim Aug 25 '18 at 05:19
  • @MdAslam Yeah I think Mehdi is right. You can just use loop in frontend to download multiple files. Can you please share your frontend code? May be i can help there? – ManishKumar Aug 27 '18 at 13:28
  • @Manish, We are not writing any front end code for this functionality. So any other project team going to call this API method and they can loop this request who is going to consume this API method, but the problem here is the SLA. When we loop in the front end to download multiple files, then it will affect the performance. I have followed a corrected answer only, but why I can't do download multiple content or part in one response. Please help me with this If you can. – Md Aslam Aug 29 '18 at 07:49
  • Can we send it as Json response? If yes then how convert the stream to Json Object/Content? – Md Aslam Sep 05 '18 at 11:41
2

I think only way is that you zip your all the files and then download one zip file. I guess you can use dotnetzip package because it is easy to use.

One way is that, you can first save your files on disk and then stream the zip to download. Another way is, you can zip them in memory and then download the file in stream

public ActionResult Download()
{
    using (ZipFile zip = new ZipFile())
    {
        zip.AddDirectory(Server.MapPath("~/Directories/hello"));

        MemoryStream output = new MemoryStream();
        zip.Save(output);
        return File(output, "application/zip", "sample.zip");
    }  
}
ManishKumar
  • 1,476
  • 2
  • 14
  • 22