Reputation: 612
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
Upvotes: 13
Views: 39803
Reputation: 21
If you want to return it as a downloadable file. Try this. Return type can be IActionResult
var theJson = JsonConvert.SerializeObject(result);
using (var zipStream = new MemoryStream())
{
using (var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Create, leaveOpen: true))
{
var entry = zipArchive.CreateEntry("data.json");
using (var entryStream = entry.Open())
using (var jsonStreamWriter = new StreamWriter(entryStream))
{
jsonStreamWriter.Write(theJson);
}
}
// Reset zip stream to point at the beginning of data
zipStream.Seek(0, SeekOrigin.Begin);
// Set the response headers
//Response.Headers.Add("Content-Disposition", "attachment; filename=compressed_response.zip");
// Return zip file containing JSON data as response
return File(zipStream.ToArray(), "application/zip");
}
Upvotes: 1
Reputation: 49
[HttpGet("api/DownloadZip")]
public async Task<IActionResult> Download()
{
var path = "C:\\test.zip";
var memory = new MemoryStream();
using (var stream = new FileStream(path, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
return File(memory, GetContentType(path), Path.GetFileName(path));
}
class Program
{
static string url = "http://localhost:5000/api/DownloadZip";
static async Task Main(string[] args)
{
var p = @"c:\temp1\test.zip";
WebClient webClient = new WebClient();
webClient.DownloadFile(new Uri(url), p);
Console.WriteLine("ENTER to exit...");
Console.ReadLine();
}
}
Upvotes: 3
Reputation: 1104
Here is my solution that worked for me
C# side
public IActionResult GetZip([FromBody] List<DocumentAndSourceDto> documents)
{
List<Document> listOfDocuments = new List<Document>();
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");
}
don't miss the ms.Position = 0;
here
Front side (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.stringify(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);
}
Now I'm able to download multiple files to zip.
Upvotes: 11
Reputation: 6916
$.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 };
}
Upvotes: 10
Reputation: 247323
The format of the value you are passing into the constructor of the MediaTypeHeaderValue
is invalid. You are also trying to add multiple content types to the header value.
Content type header takes a single type/subtype, followed by optional parameters separated with semi-colons ;
eg:
Content-Type: text/html; charset=ISO-8859-4
For your result you need to decide on which one you want to use. application/zip
or application/octet-stream
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
Also, to avoid exception you can use the MediaTypeHeaderValue.TryParse
method
var contentTypeString = "application/zip";
MediaTypeHeaderValue contentType = null;
if(MediaTypeHeaderValue.TryParse(contentTypeString, out contentType)) {
result.Content.Headers.ContentType = contentType;
}
Upvotes: 0