如何在ASP.NET WebAPI中返回文件(FileContentResult)

本文关键字:文件 返回 FileContentResult WebAPI ASP NET | 更新日期: 2023-09-27 18:24:18

在常规MVC控制器中,我们可以使用FileContentResult输出pdf。

public FileContentResult Test(TestViewModel vm)
{
    var stream = new MemoryStream();
    //... add content to the stream.
    return File(stream.GetBuffer(), "application/pdf", "test.pdf");
}

但是我们怎样才能把它变成ApiController呢?

[HttpPost]
public IHttpActionResult Test(TestViewModel vm)
{
     //...
     return Ok(pdfOutput);
}

这是我试过的,但似乎不起作用。

[HttpGet]
public IHttpActionResult Test()
{
    var stream = new MemoryStream();
    //...
    var content = new StreamContent(stream);
    content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
    content.Headers.ContentLength = stream.GetBuffer().Length;
    return Ok(content);            
}

浏览器中显示的返回结果为:

{"Headers":[{"Key":"Content-Type","Value":["application/pdf"]},{"Key":"Content-Length","Value":["152844"]}]}

还有一篇关于SO的类似文章:在ASP.NETWebneneneba API中从控制器返回二进制文件。它讨论输出现有文件。但我无法使它与溪流一起工作。

有什么建议吗?

如何在ASP.NET WebAPI中返回文件(FileContentResult)

我可以让它与ByteArrayContent一起工作,而不是将StreamContent返回为Content

[HttpGet]
public HttpResponseMessage Generate()
{
    var stream = new MemoryStream();
    // processing the stream.
    var result = new HttpResponseMessage(HttpStatusCode.OK)
    {
        Content = new ByteArrayContent(stream.ToArray())
    };
    result.Content.Headers.ContentDisposition =
        new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
    {
        FileName = "CertificationCard.pdf"
    };
    result.Content.Headers.ContentType =
        new MediaTypeHeaderValue("application/octet-stream");
    return result;
}

如果您想返回IHttpActionResult,可以这样做:

[HttpGet]
public IHttpActionResult Test()
{
    var stream = new MemoryStream();
    var result = new HttpResponseMessage(HttpStatusCode.OK)
    {
        Content = new ByteArrayContent(stream.GetBuffer())
    };
    result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
    {
        FileName = "test.pdf"
    };
    result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
    var response = ResponseMessage(result);
    return response;
}

这个问题帮助了我。

所以,试试这个:

控制器代码:

[HttpGet]
public HttpResponseMessage Test()
{
    var path = System.Web.HttpContext.Current.Server.MapPath("~/Content/test.docx");;
    HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
    var stream = new FileStream(path, FileMode.Open);
    result.Content = new StreamContent(stream);
    result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
    result.Content.Headers.ContentDisposition.FileName = Path.GetFileName(path);
    result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
    result.Content.Headers.ContentLength = stream.Length;
    return result;          
}

查看Html标记(带有点击事件和简单url):

<script type="text/javascript">
    $(document).ready(function () {
        $("#btn").click(function () {
            // httproute = "" - using this to construct proper web api links.
            window.location.href = "@Url.Action("GetFile", "Data", new { httproute = "" })";
        });
    });
</script>

<button id="btn">
    Button text
</button>
<a href=" @Url.Action("GetFile", "Data", new { httproute = "" }) ">Data</a>

这里有一个实现,它可以在不缓冲文件的情况下将文件的内容流式传输出去(如果是大文件,则以byte[]/MemoryStream等缓冲可能是服务器问题)。

public class FileResult : IHttpActionResult
{
    public FileResult(string filePath)
    {
        if (filePath == null)
            throw new ArgumentNullException(nameof(filePath));
        FilePath = filePath;
    }
    public string FilePath { get; }
    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage(HttpStatusCode.OK);
        response.Content = new StreamContent(File.OpenRead(FilePath));
        var contentType = MimeMapping.GetMimeMapping(Path.GetExtension(FilePath));
        response.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
        return Task.FromResult(response);
    }
}

它可以简单地这样使用:

public class MyController : ApiController
{
    public IHttpActionResult Get()
    {
        string filePath = GetSomeValidFilePath();
        return new FileResult(filePath);
    }
}

我不确定该归咎于哪一部分,但以下是MemoryStream不适用于您的原因:

当您向MemoryStream写入时,它会递增其Position属性。CCD_ 10的构造函数考虑流的当前CCD_。因此,如果您写入流,然后将其传递给StreamContent,则响应将从流末尾的虚无开始。

有两种方法可以正确解决这个问题:

  1. 构建内容,写入流

     [HttpGet]
     public HttpResponseMessage Test()
     {
         var stream = new MemoryStream();
         var response = Request.CreateResponse(HttpStatusCode.OK);
         response.Content = new StreamContent(stream);
         // ...
         // stream.Write(...);
         // ...
         return response;
     }
    
  2. 写入流,重置位置,构建内容

     [HttpGet]
     public HttpResponseMessage Test()
     {
         var stream = new MemoryStream();
         // ...
         // stream.Write(...);
         // ...
         stream.Position = 0;
         var response = Request.CreateResponse(HttpStatusCode.OK);
         response.Content = new StreamContent(stream);
         return response;
     }
    
  3. 如果你有一个新的流,看起来会更好,1)如果你的流不是从0 开始,会更简单

对我来说,这是之间的区别

var response = Request.CreateResponse(HttpStatusCode.OK, new StringContent(log, System.Text.Encoding.UTF8, "application/octet-stream");

var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(log, System.Text.Encoding.UTF8, "application/octet-stream");

第一个是返回StringContent的JSON表示:{"Headers":[{"Key":"Content Type","Value":["application/octet stream;charset=utf-8"]}}

而第二个正在返回正确的文件。

Request.CreateResponse似乎有一个重载,它将字符串作为第二个参数,这似乎是导致StringContent对象本身被呈现为字符串而不是实际内容的原因。

我发现这篇文章很有用:https://codeburst.io/download-files-using-web-api-ae1d1025f0a9

基本上说:

[Route("api/[controller]")]
[ApiController]
public class JobController : ControllerBase
{
    [HttpGet]
    public ActionResult GetFile()
    {
        byte[] fileContent = GetFile();
        return File(fileContent, "application/pdf", "test.pdf");
    }
}
 var memoryStream = new MemoryStream();
                await cloudFile.DownloadToStreamAsync(memoryStream);
                responseMessage.result = "Success";
                var contentType = "application/octet-stream";
            
                **using (var stream = new MemoryStream())
                {                    
                    return File(memoryStream.GetBuffer(), contentType, "Cartage.pdf");
                }**