في وحدة تحكم 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 . يتحدث عن إخراج ملف موجود. لكنني لم أستطع أن أجعلها تعمل مع جدول.
أي اقتراحات؟
بدلاً من إرجاع 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;
}
إذا كنت ترغب في العودة 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>
لست متأكدًا تمامًا من الجزء الذي يجب إلقاء اللوم عليه ، ولكن إليك لماذا 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
بالنسبة لي كان الفرق بين
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 نفسه كسلسلة ، بدلاً من المحتوى الفعلي.
إليك تطبيق يقوم بتدفق محتوى الملف دون تخزينه مؤقتًا (التخزين المؤقت بالبايت []/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);
}
}