Чтение двоичного файла и использование 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().

Другие вопросы по тегам