Utilisez MemoryStream et ZipArchive pour renvoyer un fichier zip au client dans une API web asp.net

J’essaie de renvoyer un fichier zip de l’API Web asp.net au client à l’aide du code suivant:

private byte[] CreateZip(ssortingng 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] ssortingng 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; } 

Quand j’exécute ce code, j’obtiens l’erreur suivante:

ExceptionMessage “:” Le format de la valeur ‘application / zip, application / octet-stream’ n’est pas valide. ”

c’est le code JS:

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

Une explication pourquoi c’est arrivé? Je voudrais vraiment apprécier votre aide les gars

$.ajax gère les réponses textuelles et essaiera de (utf-8) décoder le contenu: votre fichier zip n’est pas du texte, vous obtiendrez un contenu corrompu. jQuery ne prend pas en charge le contenu binary, vous devez donc utiliser ce lien et append un transport ajax sur jQuery ou utiliser directement un XmlHttpRequest. Avec un xhr, vous devez définir xhr.responseType = "blob" et lire le blob de xhr.response .

 // 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: 

avec jquery.binarytransport.js (toute bibliothèque permettant de télécharger un blob ou un ArrayBuffer fera l’affaire)

 $.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 sortinggger the download directly saveAs(blob, "example.zip"); } // [...] }); 

avec un XMLHttpRequest brut, vous pouvez voir cette question, il vous suffit d’append un xhr.responseType = "blob" pour obtenir un blob.

Je vous ai personnellement recommandé d’utiliser un transport ajax sur jQuery, c’est très simple, vous devez télécharger une bibliothèque, l’inclure dans le projet et écrire: dataType: "binary".

C’est le code de l’API, utilisant DotNetZip ( Ionic.Zip ):

  [HttpPost] public HttpResponseMessage ZipDocs([FromBody] ssortingng[] 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 }; } 

Voici ma solution qui a fonctionné pour moi

Côté C #

 public IActionResult GetZip([FromBody] List documents) { List listOfDocuments = new List(); foreach (DocumentAndSourceDto doc in documents) listOfDocuments.Add(_documentService.GetDocumentWithServerPath(doc.Id)); using (var ms = new MemoryStream()) { using (var zipArchive = new ZipArchive(ms, ZipArchiveMode.Create, true)) { foreach (var attachment in listOfDocuments) { var entry = zipArchive.CreateEntry(attachment.FileName); using (var fileStream = new FileStream(attachment.FilePath, FileMode.Open)) using (var entryStream = entry.Open()) { fileStream.CopyTo(entryStream); } } } ms.Position = 0; return File(ms.ToArray(), "application/zip"); } throw new ErrorException("Can't zip files"); } 

ne manquez pas le ms.Position = 0; ici

Face avant (angular 4):

 downloadZip(datas: any) { const headers = new Headers({ 'Content-Type': 'application/json', 'Accept': 'application/zip' }); const options = new RequestOptions({ headers: headers, withCredentials: true, responseType: ResponseContentType.ArrayBuffer }); const body = JSON.ssortingngify(datas); return this.authHttp.post(`${environment.apiBaseUrl}api/documents/zip`, body, options) .map((response: Response) => { const blob = new Blob([response.blob()], { type: 'application/zip' }); FileSaver.saveAs(blob, 'logs.zip'); }) .catch(this.handleError); } 

Maintenant, je peux télécharger plusieurs fichiers au format zip.

Le format de la valeur que vous transmettez au constructeur de MediaTypeHeaderValue n’est pas valide. Vous essayez également d’append plusieurs types de contenu à la valeur d’en-tête.

En-tête de type de contenu prend un seul type / sous-type, suivi de parameters facultatifs séparés par des points-virgules ;

par exemple:

 Content-Type: text/html; charset=ISO-8859-4 

Pour votre résultat, vous devez décider lequel vous voulez utiliser. application/zip ou application/octet-stream

 result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip"); 

De même, pour éviter les exceptions, vous pouvez utiliser la méthode MediaTypeHeaderValue.TryParse

 var contentTypeSsortingng = "application/zip"; MediaTypeHeaderValue contentType = null; if(MediaTypeHeaderValue.TryParse(contentTypeSsortingng, out contentType)) { result.Content.Headers.ContentType = contentType; }