0

I'm trying to do the following:

  1. The user fill a form and send it in .JSON to the server
  2. With the form, the server generate some .CSV files and put them all together in a .ZIP file.
  3. The server send the .ZIP file and the user download it.

After some research I have wrote this code:

My Controller:

[HttpPost]
        [Route("routeToMyAPI")]
        public HttpResponseMessage Process(Form form)
        {
            var result = new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StreamContent(<streamToMyGeneratedZipFile>)
            };

            result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
            result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
            {
                FileName = "fileName.zip"
            };

            return result;
        }

My Service:

angular.module('app')
  .factory('MyService', function ($http) {
    return {
      Process: function (form) {
        var req = $http.post('rest/example/process', form);
        return req;
      }
    };
  });

My Controller:

this.submit = function () {
    var Form = {};

    var formPost = MyService.Process(Form);
    formPost.then(function (data) {
      var a = document.createElement('a');
      var blob = new Blob([data], { 'type': "application/octet-stream" });
      a.href = URL.createObjectURL(blob);
      a.download = "fileName.zip";
      a.click();
    }, function (error) {
      alert('An error occured !');
    });
  };

I have parts 1 & 2 working, but I don't have find the way to call my ASP API to download the .ZIP file.

When I call the submit method of my Controller, I have a fileName.zip who is downloaded on my PC but when I try to open it Windows says to me that it's not a valid archive.

What did I do wrong ? I'm a rookie in angularjs and ASP so any help will be welcomed.

Thanks in advance.

Thomas Perez
  • 129
  • 2
  • 13

1 Answers1

0

Several issues with your code:

After ZipArchive does its work, the position of the stream will be at the end. So you must reset it to the beginning like this before sending it:

zipStream.Position = 0;

Since you're setting the content type and file name of the file on the server already, just parse it on the client side.

var headers = data.headers(); //$http resolves to data, status, headers, config
var regex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
var match = regex.exec(headers["content-disposition"]);
var fileName = match[1] || "myZipFile.zip";
fileName = fileName.replace(/\"/g, ""); //replace trailing and leading slashes

var contentType = headers["content-type"] || "application/octet-stream";

IE will not allow you to open blobs directly. You must use msSaveOrOpenBlob or msSaveBlob.

var blob = new Blob([data.data], { type: contentType });

  if (window.navigator && window.navigator.msSaveOrOpenBlob) {
    window.navigator.msSaveOrOpenBlob(blob, fileName);
  } else {
    var a = document.createElement('a');
    var objectUrl = URL.createObjectURL(blob);
    a.href = URL.createObjectURL(blob);
    a.download = match[1];
    a.click();
  }

One last thing: the previous code won't work on firefox because firefox doesn't support clic(). Thanks to this thread this can be solved by adding this little snippet of code:

HTMLElement.prototype.click = function() {
   var evt = this.ownerDocument.createEvent('MouseEvents');
   evt.initMouseEvent('click', true, true, this.ownerDocument.defaultView, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
   this.dispatchEvent(evt);
} 
Community
  • 1
  • 1
Kyle
  • 5,407
  • 6
  • 32
  • 47
  • Thanks, just one thing: how do you initialize the blob variable in your code ? – Thomas Perez May 03 '16 at 14:27
  • `var blob = new Blob([data.data], {type: contentType});` – Kyle May 03 '16 at 14:39
  • Ok, thank you, the .ZIP file is fully returned but there is one thing: the file downloaded with a very strange name (for example: "6b046718-2d12-4d6e-9eef-90b4afa04628") and has no extension at all. Do you have any idea how can I fix this ? – Thomas Perez May 03 '16 at 14:52
  • Instead of calling `ContentDispositionHeaderValue`, try simply calling `System.Net.Mime.ContentDisposition`. Use it like this: `var cd = new System.Net.Mime.ContentDisposition { FileName = "fileName.zip", Inline = true }; result.Content.Headers.ContentDisposition = cd.ToString();` – Kyle May 03 '16 at 14:58
  • Or, look in your `regex` matches. What position in `match` contains your file name? – Kyle May 03 '16 at 14:59
  • I can't assign a `String` to `result.Content.Headers.ContentDisposition` and the name of the file is in the first bucket of the array. – Thomas Perez May 03 '16 at 15:04
  • Then take away `ToString();` – Kyle May 03 '16 at 15:06
  • It seems that a `System.Net.Mime.ContentDisposition` cannot be converted into `System.Net.Http.Headers.ContentDispositionHeaderValue`. – Thomas Perez May 03 '16 at 15:10
  • No idea, dude. If this is is the answer, please mark as an answer. If you have more issues, please post a new question. – Kyle May 03 '16 at 15:12
  • I think I know why the filename is wrong. I'm testing my code with Chrome and Chrome doesn't support `msSaveOrOpenBlob` who use `fileName`. When I test my code with IE the file has the expected name. – Thomas Perez May 03 '16 at 15:13
  • Fixed: `if (window.navigator && window.navigator.msSaveOrOpenBlob) { window.navigator.msSaveOrOpenBlob(blob, fileName); } else { var a = document.createElement('a'); var objectUrl = URL.createObjectURL(blob); a.href = URL.createObjectURL(blob); a.download = match[1]; a.click(); }` thank you very much dude :-D – Thomas Perez May 03 '16 at 15:19