Search code examples
c#ajaxasp.net-web-apimemorystreamc#-ziparchive

Use MemoryStream and ZipArchive to return zip file to client in asp.net web api


I am trying to return zip file from asp.net web api to client using The following Code:

private byte[] CreateZip(string data)
{
    using (var ms = new MemoryStream())
    {
        using (var ar = new ZipArchive(ms, ZipArchiveMode.Create, true))
        {
            var file = archive.CreateEntry("file.html");

            using (var entryStream = file.Open())
            using (var sw = new StreamWriter(entryStream))
            {
                sw .Write(value);
            }
        }
        return memoryStream.ToArray();
    }
}

public HttpResponseMessage Post([FromBody] string data)
{
    HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
    result.Content = new ByteArrayContent(CreateZip(data));
    result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip, application/octet-stream");
    return result;
}

When i run this code i get the following error:

ExceptionMessage":"The format of value 'application/zip, application/octet-stream' is invalid."

this is the JS code:

$.ajax({
  type: "POST",
  url: url,
  data: data,
  dataType: application/x-www-form-urlencoded
});

Any explanation why this is happen? I would really appriciate your help guys


Solution

  • $.ajax handles text responses and will try to (utf-8) decode the content: your zip file isn't text, you will get a corrupted content. jQuery doesn't support binary content so you need to use this link and add an ajax transport on jQuery or use directly a XmlHttpRequest. With an xhr, you need to set xhr.responseType = "blob" and read from xhr.response the blob.

    // with xhr.responseType = "arraybuffer"
    var arraybuffer = xhr.response;
    var blob = new Blob([arraybuffer], {type:"application/zip"});
    saveAs(blob, "example.zip");
    
    // with xhr.responseType = "blob"
    var blob = xhr.response;
    saveAs(blob, "example.zip");
    Edit: examples:
    

    with jquery.binarytransport.js (any library that let you download a Blob or an ArrayBuffer will do)

    $.ajax({
      url: url,
      type: "POST",
      contentType: "application/json",
      dataType: "binary", // to use the binary transport
      // responseType:'blob', this is the default
      data: data,
      processData: false,
      success: function (blob) {
        // the result is a blob, we can trigger the download directly
        saveAs(blob, "example.zip");
      }
      // [...]
    });
    

    with a raw XMLHttpRequest, you can see this question, you just need to add a xhr.responseType = "blob" to get a blob.

    I personally recommended you to use an ajax transport on jQuery, that's very easy, you have to download a library, include it in the project and write: dataType: "binary".

    This is the API code, using DotNetZip (Ionic.Zip):

       [HttpPost]
        public HttpResponseMessage ZipDocs([FromBody] string[] docs)
        {
            using (ZipFile zip = new ZipFile())
            {
                //this code takes an array of documents' paths and Zip them
                zip.AddFiles(docs, false, "");
                return ZipContentResult(zip);
            }
        }
    
        protected HttpResponseMessage ZipContentResult(ZipFile zipFile)
        {
            var pushStreamContent = new PushStreamContent((stream, content, context) =>
            {
              zipFile.Save(stream);
                stream.Close(); 
            }, "application/zip");
    
            return new HttpResponseMessage(HttpStatusCode.OK) { Content = pushStreamContent };
        }