如何从dotnet核心webapi下载ZipFile? [英] How to download a ZipFile from a dotnet core webapi?

查看:183
本文介绍了如何从dotnet核心webapi下载ZipFile?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从一个dotnet核心web api操作下载一个zip文件,但是我无法使其工作。我试图通过POSTMAN和我的Aurelia Http获取客户端来调用该操作。



我可以像我想要的那样创建ZipFile并将其存储在系统上, t修复它,所以它通过api返回zipfile。



用例:用户选择几个图片集并点击下载按钮。图片集的ids将发送到api,并创建一个zipfile,其中包含用于保存图片的每个图片集合的目录。该zip文件返回给用户,以便他/她可以将其存储在他们的系统上。



任何帮助将不胜感激。



我的控制器操作

  ///< summary> 
///下载图片集合及其图片的集合
///< / summary>
///< param name =ids>要下载的集合的ids< / param>
///< returns>< / returns>
[HttpPost(download)]
[ProducesResponseType(typeof(void),(int)HttpStatusCode.OK)]
public async任务< IActionResult>下载([FromBody] IEnumerable< int> ids)
{
//创建新的zipfile
var zipFile = ${_ ApiSettings.Pictures.AbsolutePath} / collections_download_ {Guid.NewGuid()。 。的ToString( N)子串(0,5)}拉链。

使用(var repo = new PictureCollectionsRepository())
using(var picturesRepo = new PicturesRepository())
using(var archive = ZipFile.Open(zipFile,ZipArchiveMode.Create )
{
foreach(ids中的var id)
{
//获取集合和图片
var collection = await repo.Get(id);
var pictures = await picturesRepo
.GetAll()
.Where(x => x.CollectionId == collection.Id)
.ToListAsync();

//创建集合目录重要信息:尾部斜杠
var directory = ${collection.Number} _ {collection.Name} _ {collection.Date:yyyy-MM-dd} /;
archive.CreateEntry(directory);

//将图片添加到当前集合目录
pictures.ForEach(x => archive.CreateEntryFromFile(x.FilePath,${directory} / {x.FileName} ));
}
}

//在这里做什么,它返回刚创建的zip文件?
}
}

我的aurelia提取客户端功能: strong>

  / ** 
*从ids数组中的图片集中下载所有图片
* @params ids要下载的图片集的ids
* /
download(ids:Array< number>):Promise< any> {
return this.http.fetch(AppConfiguration.baseUrl + this.controller +'download',{
method:'POST',
body:json(ids)
} )
}

我尝试了



请注意,我所尝试的不会产生错误,它似乎不会做任何事情。



1 )创建我自己的FileResult(就像我以前和旧版ASP.NET一样)。当我通过邮递员或应用程序调用时,看不到标题被使用。

 返回新的FileResult(zipFile, Path.GetFileName(zipFile),application / zip); 

public class FileResult:IActionResult
{
private readonly string _filePath;
private readonly string _contentType;
私有readonly字符串_fileName;

public FileResult(string filePath,string fileName =,string contentType = null)
{
if(filePath == null)throw new ArgumentNullException(nameof(filePath)) ;

_filePath = filePath;
_contentType = contentType;
_fileName = fileName;
}

public Task ExecuteResultAsync(ActionContext context)
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content =新的ByteArrayContent(System.IO.File.ReadAllBytes(_filePath))
};

if(!string.IsNullOrEmpty(_fileName))
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue(attachment)
{
FileName = _fileName
};

response.Content.Headers.ContentType = new MediaTypeHeaderValue(_contentType);

return Task.FromResult(response);
}
}

}



2) https://stackoverflow.com/a/34857134/2477872



不需要。

  HttpContext.Response.ContentType =application / zip; 
var result = new FileContentResult(System.IO.File.ReadAllBytes(zipFile),application / zip)
{
FileDownloadName = Path.GetFileName(zipFile)
};
返回结果;

我已经尝试了一个测试虚拟PDF文件,似乎与POSTMAN一起工作。但是当我尝试将其改为zipfile(见上文)时,它不会执行任何操作。

  HttpContext.Response.ContentType =application / PDF; 
var result = new FileContentResult(System.IO.File.ReadAllBytes(PATH / test.pdf),application / pdf)
{
FileDownloadName =test.pdf
};

返回结果;


解决方案

如何轻松地通过一个dotnet-core api来容纳一个PDF以及一个ZIP:

  ///< summary> 
///将文件提供为PDF格式。
///< / summary>
[HttpGet,Route({filename} / pdf,Name =GetPdfFile)]
public IActionResult GetPdfFile(string filename)
{
const string contentType =application / PDF;
HttpContext.Response.ContentType = contentType;
var result = new FileContentResult(System.IO.File.ReadAllBytes(@{path_to_files} \file.pdf),contentType)
{
FileDownloadName = ${filename}。 pdf
};

返回结果;
}

///< summary>
///将文件作为ZIP提供。
///< / summary>
[HttpGet,Route({filename} / zip,Name =GetZipFile)]
public IActionResult GetZipFile(string filename)
{
const string contentType =application /压缩;
HttpContext.Response.ContentType = contentType;
var result = new FileContentResult(System.IO.File.ReadAllBytes(@{path_to_files} \file.zip),contentType)
{
FileDownloadName = ${filename}。 zip
};

返回结果;
}

此示例只是工作和交易;注意在这种情况下,两个动作之间只有一个主要区别(当然包含了源文件名):返回的contentType。



上面的示例使用应用程序/ zip,如您所提及的,但可能只需要提供不同的mimetype(如application / octet * )。



这导致推测无法正确读取zip文件,或者您的网络服务器配置可能未正确配置以供投放.zip文件。



后者可能会因您是否运行IIS Express,IIS,kestrel等而有所不同,但要将其测试,您可以尝试向您的〜/ wwwroot文件夹,确保您已启用在Status.cs中提供静态文件,以查看是否可以直接下载文件。


I am trying to download a zip file from a dotnet core web api action, but I can't make it work. I tried calling the action via POSTMAN and my Aurelia Http Fetch Client.

I am able to create the ZipFile like I want it and store it on the system, but can't fix it so it returns the zipfile via the api.

Use-case: User selects a couple of picture collections and clicks the download button. The ids of the picture collections gets send to the api and a zipfile is created which contains a directory for every picture collection which holds the pictures. That zipfile is returned to the user so he/she can store it on their system.

Any help would be appreciated.

My controller action

      /// <summary>
      /// Downloads a collection of picture collections and their pictures
      /// </summary>
      /// <param name="ids">The ids of the collections to download</param>
      /// <returns></returns>
      [HttpPost("download")]
      [ProducesResponseType(typeof(void), (int) HttpStatusCode.OK)]
      public async Task<IActionResult> Download([FromBody] IEnumerable<int> ids)
      {
           // Create new zipfile
           var zipFile = $"{_ApiSettings.Pictures.AbsolutePath}/collections_download_{Guid.NewGuid().ToString("N").Substring(0,5)}.zip";

           using (var repo = new PictureCollectionsRepository())
           using (var picturesRepo = new PicturesRepository())
           using (var archive = ZipFile.Open(zipFile, ZipArchiveMode.Create))
           {
                foreach (var id in ids)
                {
                     // Fetch collection and pictures
                     var collection = await repo.Get(id);
                     var pictures = await picturesRepo
                          .GetAll()
                          .Where(x => x.CollectionId == collection.Id)
                          .ToListAsync();

                     // Create collection directory IMPORTANT: the trailing slash
                     var directory = $"{collection.Number}_{collection.Name}_{collection.Date:yyyy-MM-dd}/";
                     archive.CreateEntry(directory);

                     // Add the pictures to the current collection directory
                     pictures.ForEach(x => archive.CreateEntryFromFile(x.FilePath, $"{directory}/{x.FileName}"));
                }
           }

           // What to do here so it returns the just created zip file?
      }
 }

My aurelia fetch client function:

/**
 * Downloads all pictures from the picture collections in the ids array
 * @params ids The ids of the picture collections to download
 */
download(ids: Array<number>): Promise<any> {
    return this.http.fetch(AppConfiguration.baseUrl + this.controller + 'download', {
        method: 'POST',
        body: json(ids)
    })
}

What I've tried

Note that what I've tried does not generate errors, it just doesn't seems to do anything.

1) Creating my own FileResult (like I used to do with older ASP.NET). Can't see the headers being used at all when I call it via postman or the application.

return new FileResult(zipFile, Path.GetFileName(zipFile), "application/zip");

 public class FileResult : IActionResult
 {
      private readonly string _filePath;
      private readonly string _contentType;
      private readonly string _fileName;

      public FileResult(string filePath, string fileName = "", string contentType = null)
      {
           if (filePath == null) throw new ArgumentNullException(nameof(filePath));

           _filePath = filePath;
           _contentType = contentType;
           _fileName = fileName;
      }

      public Task ExecuteResultAsync(ActionContext context)
      {
           var response = new HttpResponseMessage(HttpStatusCode.OK)
           {
                Content = new ByteArrayContent(System.IO.File.ReadAllBytes(_filePath))
           };

           if (!string.IsNullOrEmpty(_fileName))
                response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
                {
                     FileName = _fileName
                };

           response.Content.Headers.ContentType = new MediaTypeHeaderValue(_contentType);

           return Task.FromResult(response);
      }
 }

}

2) https://stackoverflow.com/a/34857134/2477872

Does nothing.

      HttpContext.Response.ContentType = "application/zip";
           var result = new FileContentResult(System.IO.File.ReadAllBytes(zipFile), "application/zip")
           {
                FileDownloadName = Path.GetFileName(zipFile)
           };
           return result;

I've tried it with a test dummy PDF file and that seemed to work with POSTMAN. But when I try to change it to the zipfile (see above) it does nothing.

  HttpContext.Response.ContentType = "application/pdf";
           var result = new FileContentResult(System.IO.File.ReadAllBytes("THE PATH/test.pdf"), "application/pdf")
           {
                FileDownloadName = "test.pdf"
           };

           return result;

解决方案

To put a long story short, the example below illustrates how to easily serve both a PDF as well as a ZIP through a dotnet-core api:

/// <summary>
/// Serves a file as PDF.
/// </summary>
[HttpGet, Route("{filename}/pdf", Name = "GetPdfFile")]
public IActionResult GetPdfFile(string filename)
{
    const string contentType = "application/pdf";
    HttpContext.Response.ContentType = contentType;
    var result = new FileContentResult(System.IO.File.ReadAllBytes(@"{path_to_files}\file.pdf"), contentType)
    {
        FileDownloadName = $"{filename}.pdf"
    };

    return result;
}

/// <summary>
/// Serves a file as ZIP.
/// </summary>
[HttpGet, Route("{filename}/zip", Name = "GetZipFile")]
public IActionResult GetZipFile(string filename)
{
    const string contentType ="application/zip";
    HttpContext.Response.ContentType = contentType;
    var result = new FileContentResult(System.IO.File.ReadAllBytes(@"{path_to_files}\file.zip"), contentType)
    {
        FileDownloadName = $"{filename}.zip"
    };

    return result;
}

This sample just works™

Notice in this case there is only one main difference between the two actions (besied the source file name, of course): the contentType that is returned.

The example above uses 'application/zip', as you've mentioned yourself, but it might just be required to serve a different mimetype (like 'application/octet*').

This leads to the speculation that either the zipfile cannot be read properly or that your webserver configuration might not be configured properly for serving .zip files.

The latter may differ based on whether you're running IIS Express, IIS, kestrel etc. But to put this to the test, you could try adding a zipfile to your ~/wwwroot folder, making sure you have enabled serving static files in your Status.cs, to see if you can download the file directly.

这篇关于如何从dotnet核心webapi下载ZipFile?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆