Попытка передать файл PDF потоком с asp.net производит “поврежденный файл”

Система. Поточная обработка. Поток. CurrentThread. CurrentCulture

7
задан Pablo 19 August 2009 в 15:30
поделиться

4 ответа

Вот метод, который я использую. Это возвращает вложение, поэтому IE создает диалоговое окно «Открыть / сохранить». Я также знаю, что файлы не будут больше 1M, поэтому я уверен, что есть более чистый способ сделать это.

У меня была аналогичная проблема с PDF-файлами, и я понял, что мне абсолютно необходимо использовать двоичные потоки и ReadBytes. Все, что связано со строками, испортило его.

Stream stream = GetStream(); // Assuming you have a method that does this.
BinaryReader reader = new BinaryReader(stream);

HttpResponse response = HttpContext.Current.Response;
response.ContentType = "application/pdf";
response.AddHeader("Content-Disposition", "attachment; filename=file.pdf");
response.ClearContent();
response.OutputStream.Write(reader.ReadBytes(1000000), 0, 1000000);

// End the response to prevent further work by the page processor.
response.End();
7
ответ дан 6 December 2019 в 14:06
поделиться

У меня есть нечто подобное, работающее над текущим веб-сайтом 2.0. И я помню, как пытался заставить его работать, хотя прошло какое-то время, поэтому я не помню, как это было.

Однако есть несколько отличий между тем, что у меня есть, что есть у вас. Надеюсь, это поможет вам решить проблему.

  • После вызова ClearContent я вызываю ClearHeaders ();
  • Я не указываю длину.
  • Я не указываю встроенные в Disposition
  • I знаю, что все говорят не делать этого, но у меня есть Response.Flush (); за которым следует Response.Close ();

И еще одно:

0
ответ дан 6 December 2019 в 14:06
поделиться

Этот фрагмент сделал это за меня:

                Response.Clear();
                Response.ClearContent();
                Response.ClearHeaders();
                Response.ContentType = mimeType;
                Response.AddHeader("Content-Disposition", "attachment");
                Response.WriteFile(filePath);
                Response.Flush();
1
ответ дан 6 December 2019 в 14:06
поделиться

Другие варианты копируют содержимое файла в память перед отправкой ответа. Если данные уже находятся в памяти, то у вас будет две копии, что не очень хорошо для масштабируемости. Вместо этого лучше использовать следующее:

public void SendFile(Stream inputStream, long contentLength, string mimeType, string fileName)
{
    string clength = contentLength.ToString(CultureInfo.InvariantCulture);
    HttpResponse response = HttpContext.Current.Response;
    response.ContentType = mimeType;
    response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
    if (contentLength != -1) response.AddHeader("Content-Length", clength);
    response.ClearContent();
    inputStream.CopyTo(response.OutputStream);
    response.OutputStream.Flush();
    response.End();
}

Поскольку поток - это набор байтов, нет необходимости использовать BinaryReader. И пока входной поток заканчивается в конце файла, вы можете просто использовать метод CopyTo() для потока, который вы хотите отправить в веб-браузер. Все содержимое будет записано в целевой поток без создания каких-либо промежуточных копий данных.

Если вам нужно скопировать только определенное количество байт из потока, вы можете создать метод расширения, который добавит еще пару перегрузок CopyTo():

public static class Extensions
{
    public static void CopyTo(this Stream inStream, Stream outStream, long length)
    {
        CopyTo(inStream, outStream, length, 4096);
    }

    public static void CopyTo(this Stream inStream, Stream outStream, long length, int blockSize)
    {
        byte[] buffer = new byte[blockSize];
        long currentPosition = 0;

        while (true)
        {
            int read = inStream.Read(buffer, 0, blockSize);
            if (read == 0) break;
            long cPosition = currentPosition + read;
            if (cPosition > length) read = read - Convert.ToInt32(cPosition - length);
            outStream.Write(buffer, 0, read);
            currentPosition += read;
            if (currentPosition >= length) break;
        }
    }
}

Затем вы можете использовать его следующим образом:

inputStream.CopyTo(response.OutputStream, contentLength);

Это будет работать с любым входным потоком, но в качестве примера можно привести чтение файла из файловой системы:

string filename = @"C:\dirs.txt";
using (FileStream fs = File.Open(filename, FileMode.Open))
{
    SendFile(fs, fs.Length, "application/octet-stream", filename);
}

Как уже говорилось, убедитесь, что ваш MIME-тип соответствует содержимому.

7
ответ дан 6 December 2019 в 14:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: