it-swarm.asia

كيفية إرجاع ملف (FileContentResult) في ASP.NET WebAPI

في وحدة تحكم MVC منتظمة ، يمكننا إخراج قوات الدفاع الشعبي مع FileContentResult.

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.NET Web API . يتحدث عن إخراج ملف موجود. لكنني لم أستطع أن أجعلها تعمل مع جدول.

أي اقتراحات؟

146
Blaise

بدلاً من إرجاع StreamContent كـ Content ، يمكنني جعله يعمل مع ByteArrayContent.

[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;
}
168
Blaise

إذا كنت ترغب في العودة 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;
}
76
Ogglas

هذا السؤال ساعدني.

لذا جرب هذا:

كود تحكم:

[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>
40
aleha

لست متأكدًا تمامًا من الجزء الذي يجب إلقاء اللوم عليه ، ولكن إليك لماذا MemoryStream لا يعمل من أجلك:

أثناء الكتابة إلى MemoryStream ، تزيد من خاصية Position. يأخذ مُنشئ StreamContent في الاعتبار تيار الدفق الحالي Position. لذلك إذا كتبت إلى الدفق ، ثم نقلته إلى 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;
}

2) يبدو أفضل قليلاً إذا كان لديك بث مباشر ، 1) يكون أبسط إذا لم يبدأ التدفق في 0

6
M.Stramm

بالنسبة لي كان الفرق بين

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");

أول واحد كان إرجاع تمثيل JSON لـ StringContent: {"Headers": [{"Key": "Content-Type"، "Value": ["application/octet-stream؛ charset = utf-8"]}]}

بينما كان الثاني يعيد الملف الصحيح.

يبدو أن Request.CreateResponse يحتوي على تحميل زائد يأخذ سلسلة كمعلمة ثانية ، ويبدو أن هذا هو ما كان يتسبب في تقديم كائن StringContent نفسه كسلسلة ، بدلاً من المحتوى الفعلي.

3
EnderWiggin

إليك تطبيق يقوم بتدفق محتوى الملف دون تخزينه مؤقتًا (التخزين المؤقت بالبايت []/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);
    }
}
3
Simon Mourier