使用 MemoryStream 和 ZipArchive return 将文件压缩到 asp.net web api 中的客户端
Use MemoryStream and ZipArchive to return zip file to client in asp.net web api
我正在尝试 return 使用以下代码将文件从 asp.net 网络 api 压缩到客户端:
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;
}
当我 运行 此代码时,我收到以下错误:
ExceptionMessage":"The format of value 'application/zip,
application/octet-stream' is invalid."
这是JS代码:
$.ajax({
type: "POST",
url: url,
data: data,
dataType: application/x-www-form-urlencoded
});
任何解释为什么会这样?我真的很感谢你们的帮助
您传递给 MediaTypeHeaderValue
的构造函数的值的格式无效。您还试图将多种内容类型添加到 header 值。
内容类型header采用单个type/subtype,后跟用semi-colons;
分隔的可选参数
例如:
Content-Type: text/html; charset=ISO-8859-4
对于您的结果,您需要决定要使用哪一个。 application/zip
或 application/octet-stream
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
此外,为避免异常,您可以使用 MediaTypeHeaderValue.TryParse
方法
var contentTypeString = "application/zip";
MediaTypeHeaderValue contentType = null;
if(MediaTypeHeaderValue.TryParse(contentTypeString, out contentType)) {
result.Content.Headers.ContentType = contentType;
}
$.ajax
处理文本响应并将尝试 (utf-8) 解码内容:您的 zip 文件不是文本,您将得到损坏的内容。 jQuery 不支持二进制内容,因此您需要使用 this link 并在 jQuery 上添加 ajax 传输或直接使用 XmlHttpRequest。使用 xhr,您需要设置 xhr.responseType = "blob"
并从 xhr.response
读取 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(任何允许您下载 Blob 或 ArrayBuffer 的库都可以)
$.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");
}
// [...]
});
使用原始 XMLHttpRequest,您可以看到 this 问题,您只需添加一个 xhr.responseType = "blob"
即可获得一个 blob。
我个人建议你在jQuery上使用ajax传输,这很简单,你必须下载一个库,将其包含在项目中并写:dataType: "binary".
这是 API 代码,使用 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 };
}
这是对我有用的解决方案
C#端
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");
}
不要错过这里的ms.Position = 0;
正面 (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);
}
现在我可以将多个文件下载到 zip。
这个适合asp.net核心版本
[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));
}
然后使用Web客户端调用
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();
}
}
我正在尝试 return 使用以下代码将文件从 asp.net 网络 api 压缩到客户端:
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;
}
当我 运行 此代码时,我收到以下错误:
ExceptionMessage":"The format of value 'application/zip, application/octet-stream' is invalid."
这是JS代码:
$.ajax({
type: "POST",
url: url,
data: data,
dataType: application/x-www-form-urlencoded
});
任何解释为什么会这样?我真的很感谢你们的帮助
您传递给 MediaTypeHeaderValue
的构造函数的值的格式无效。您还试图将多种内容类型添加到 header 值。
内容类型header采用单个type/subtype,后跟用semi-colons;
例如:
Content-Type: text/html; charset=ISO-8859-4
对于您的结果,您需要决定要使用哪一个。 application/zip
或 application/octet-stream
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
此外,为避免异常,您可以使用 MediaTypeHeaderValue.TryParse
方法
var contentTypeString = "application/zip";
MediaTypeHeaderValue contentType = null;
if(MediaTypeHeaderValue.TryParse(contentTypeString, out contentType)) {
result.Content.Headers.ContentType = contentType;
}
$.ajax
处理文本响应并将尝试 (utf-8) 解码内容:您的 zip 文件不是文本,您将得到损坏的内容。 jQuery 不支持二进制内容,因此您需要使用 this link 并在 jQuery 上添加 ajax 传输或直接使用 XmlHttpRequest。使用 xhr,您需要设置 xhr.responseType = "blob"
并从 xhr.response
读取 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(任何允许您下载 Blob 或 ArrayBuffer 的库都可以)
$.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");
}
// [...]
});
使用原始 XMLHttpRequest,您可以看到 this 问题,您只需添加一个 xhr.responseType = "blob"
即可获得一个 blob。
我个人建议你在jQuery上使用ajax传输,这很简单,你必须下载一个库,将其包含在项目中并写:dataType: "binary".
这是 API 代码,使用 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 };
}
这是对我有用的解决方案
C#端
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");
}
不要错过这里的ms.Position = 0;
正面 (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);
}
现在我可以将多个文件下载到 zip。
这个适合asp.net核心版本
[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));
}
然后使用Web客户端调用
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();
}
}