问题
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
回答1:
$.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 };
}
回答2:
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.
回答3:
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;
}
回答4:
This is suitable for asp.net core version.
[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));
}
Then use Web client call
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();
}
}
来源:https://stackoverflow.com/questions/37530252/use-memorystream-and-ziparchive-to-return-zip-file-to-client-in-asp-net-web-api