web-dev-qa-db-ja.com

MemoryStreamとZipArchiveを使用して、zipファイルをasp.net WebAPIのクライアントに返します。

次のコードを使用して、asp.net WebAPIからクライアントにZipファイルを返そうとしています。

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 ":"値 'application/Zip、application/octet-stream'の形式が無効です。 "

これはJSコードです:

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

なぜこれが起こるのか説明はありますか?私は本当にあなたの助けの人に感謝します

6
Kob_24

$.ajaxはテキスト応答を処理し、コンテンツを(utf-8)デコードしようとします。Zipファイルはテキストではないため、破損したコンテンツが表示されます。 jQueryはバイナリコンテンツをサポートしていないため、 this リンクを使用して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"を追加する必要があります。

個人的には、jQueryでajaxトランスポートを使用することをお勧めします。これは非常に簡単です。ライブラリをダウンロードしてプロジェクトに含め、次のように記述する必要があります。dataType: "binary".

これは、DotNetZip(Ionic.Zip)を使用したAPIコードです。

   [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 };
    }
9
user3378165

これが私のために働いた私の解決策です

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;をお見逃しなく

前面(角度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にダウンロードできるようになりました。

6
Fitch

MediaTypeHeaderValueのコンストラクターに渡す値の形式が無効です。また、ヘッダー値に複数のコンテンツタイプを追加しようとしています。

コンテンツタイプヘッダーは単一のタイプ/サブタイプを取り、その後にセミコロンで区切られたオプションのパラメーター;

例えば:

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;
}
0
Nkosi

これは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();
        }
    }
0
Ken