Чтение двоичного файла и использование Response.BinaryWrite()
У меня есть приложение, которое должно прочитать файл PDF из файловой системы, а затем записать его пользователю. PDF 183 КБ и, кажется, работает отлично. Когда я использую код внизу, браузер получает файл размером 224 КБ, и я получаю сообщение от Acrobat Reader о том, что файл поврежден и не может быть восстановлен.
Вот мой код (я также пытался использовать File.ReadAllBytes(), но я получаю то же самое):
using (FileStream fs = File.OpenRead(path))
{
int length = (int)fs.Length;
byte[] buffer;
using (BinaryReader br = new BinaryReader(fs))
{
buffer = br.ReadBytes(length);
}
Response.Clear();
Response.Buffer = true;
Response.AddHeader("content-disposition", String.Format("attachment;filename={0}", Path.GetFileName(path)));
Response.ContentType = "application/" + Path.GetExtension(path).Substring(1);
Response.BinaryWrite(buffer);
}
10 ответов
Попробуйте добавить
Response.End();
после вызова Response.BinaryWrite().
Вы можете случайно отправить другой контент после Response.BinaryWrite, что может запутать браузер. Response.End будет гарантировать, что браузер получит только то, что вы действительно хотите.
Response.BinaryWrite(bytes);
Response.Flush();
Response.Close();
Response.End();
Это работает для нас. Мы создаем PDF-файлы из служб отчетов SQL.
Мы использовали это с большим успехом. WriteFile сделать для загрузки для вас и Flush / End в конце, чтобы отправить все это клиенту.
//Use these headers to display a saves as / download
//Response.ContentType = "application/octet-stream";
//Response.AddHeader("Content-Disposition", String.Format("attachment; filename={0}.pdf", Path.GetFileName(Path)));
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", String.Format("inline; filename={0}.pdf", Path.GetFileName(Path)));
Response.WriteFile(path);
Response.Flush();
Response.End();
Поскольку вы отправляете файл напрямую из вашей файловой системы без промежуточной обработки, почему бы не использовать Response.TransmitFile
вместо?
Response.Clear();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition",
"attachment; filename=\"" + Path.GetFileName(path) + "\"");
Response.TransmitFile(path);
Response.End();
(Я подозреваю, что ваша проблема вызвана отсутствием Response.End
Это означает, что вы отправляете остальную часть содержимого своей страницы, добавленную к данным PDF.)
Просто для дальнейшего использования, как указано в этом сообщении в блоге: http://blogs.msdn.com/b/aspnetue/archive/2010/05/25/response-end-response-close-and-how-customer-feedback-helps-us-improve-msdn-documentation.aspx
Не рекомендуется звонить Response.Close()
или же Response.End()
- вместо этого используйте CompleteRequest()
,
Ваш код будет выглядеть примерно так:
byte[] bytes = {};
bytes = GetBytesFromDB(); // I use a similar way to get pdf data from my DB
Response.Clear();
Response.ClearHeaders();
Response.Buffer = true;
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.ContentType = "application/pdf";
Response.AppendHeader("Content-Disposition", "attachment; filename=" + anhangTitel);
Response.AppendHeader("Content-Length", bytes.Length.ToString());
this.Context.ApplicationInstance.CompleteRequest();
Пожалуйста, прочтите это перед использованием Response.TransmitFile: http://improve.dk/blog/2008/03/29/response-transmitfile-close-will-kill-your-application
Возможно, вам не хватает Response.close, чтобы закрыть de Binary Stream
В моем приложении MVC я включил сжатие gzip для всех ответов. Если вы читаете эту двоичную запись из ajax-вызова с gzipped ответами, вы получаете gzipped bytearray, а не оригинальный bytearray, с которым вам нужно работать.
//c# controller is compressing the result after the response.binarywrite
[compress]
public ActionResult Print(int id)
{
...
var byteArray=someService.BuildPdf(id);
return return this.PDF(byteArray, "test.pdf");
}
//where PDF is a custom actionresult that eventually does this:
public class PDFResult : ActionResult
{
...
public override void ExecuteResult(ControllerContext context)
{
//Set the HTTP header to excel for download
HttpContext.Current.Response.Clear();
//HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";
HttpContext.Current.Response.ContentType = "application/pdf";
HttpContext.Current.Response.AddHeader("content-disposition", string.Concat("attachment; filename=", fileName));
HttpContext.Current.Response.AddHeader("Content-Length", pdfBytes.Length.ToString());
//Write the pdf file as a byte array to the page
HttpContext.Current.Response.BinaryWrite(byteArray);
HttpContext.Current.Response.End();
}
}
//javascript
function pdf(mySearchObject) {
return $http({
method: 'Post',
url: '/api/print/',
data: mySearchObject,
responseType: 'arraybuffer',
headers: {
'Accept': 'application/pdf',
}
}).then(function (response) {
var type = response.headers('Content-Type');
//if response.data is gzipped, this blob will be incorrect. you have to uncompress it first.
var blob = new Blob([response.data], { type: type });
var fileName = response.headers('content-disposition').split('=').pop();
if (window.navigator.msSaveOrOpenBlob) { // for IE and Edge
window.navigator.msSaveBlob(blob, fileName);
} else {
var anchor = angular.element('<a/>');
anchor.css({ display: 'none' }); // Make sure it's not visible
angular.element(document.body).append(anchor); // Attach to document
anchor.attr({
href: URL.createObjectURL(blob),
target: '_blank',
download: fileName
})[0].click();
anchor.remove();
}
});
}
"var blob = new Blob ([response.data], {type: type});" Это даст вам тот недействительный / поврежденный файл, который вы пытаетесь открыть, когда вы превращаете этот байтовый массив в файл в вашем JavaScript, если вы не распаковывайте это сначала.
Чтобы это исправить, у вас есть возможность либо запретить распаковывание этих двоичных данных, чтобы вы могли правильно превратить их в загружаемый файл, либо распаковать эти сжатые данные в коде JavaScript перед тем, как превратить их в файл.
Я также счел необходимым добавить следующее:
Response.Encoding = Encoding.Default
Если я не включил это, мой JPEG был поврежден и удвоил размер в байтах.
Но только если обработчик возвращался со страницы ASPX. Казалось, работает с ASHX это не требуется.
В дополнение к Игорю Response.Close(), я бы добавил Response.Flush().