Ответы с форумов MSDN

Windows Forms - Отобразить PDF в WebBrowser

Date: 02.03.2017 13:34:51

Во-первых, чтобы вообще отобразить PDF-файл в WebBrowser, необходимо наличие на целевой машине соответствующего расширения Internet Explorer, работоспособного в используемой версии (по умолчанию, компонент webBrowser использует более старую версию IE чем установленная на машине). Иначе IE просто предложит пользователю скачать файл.

Во-вторых, отобразить документ из базы данных в WebBrowser, конечно, можно. Для этого нужно унаследовать свой класс от Stream, реализовав логику загрузки из БД, и присвоить объект этого класса свойству WebBrowser.DocumentStream

В-третьих, как документ будет загружаться - поточно или целиком в память - полностью зависит от используемого для данного документа расширения IE. Сам WebBrowser ничего не знает о PDF.

Message 917

Date: 02.03.2017 15:07:01

Свойство  WebBrowser.DocumentStream как раз и позволяет решить вопрос загрузки документа из памяти. 

Если производительность для вас не важна, вы можете просто выгрузить файл целиком как массив байт, создать на основе него MemoryStream, и скормить его веб-браузеру:

OleDbDataReader rd; /*Выполнить SQL запрос...*/

rd.Read();

byte[] array=(byte[])rd["Data"]; Stream ms=new MemoryStream(array); webBrowser.DocumentStream=ms;

Правильнее, конечно, создать свой класс, производный от Stream, и считывать данные маленькими порциями из DataReader, чтобы избежать выделения слишком большого блока памяти. 

Другое дело, как это все сработает именно для формата PDF? Ведь WebBrouser изначально не предназначен для его отображения. Наверное, лучше использовать специализированные компоненты для отображения PDF.

Message 915

Date: 03.03.2017 8:12:39

Вы понимаете, что я свое первое сообщение не просто так писал?

Напишите, какое расширение IE установлено для просмотра PDF.

Message 914

Date: 03.03.2017 13:00:49

Угу. Я подумаю, как решить эту проблему.

Скорее всего, придется использовать обходной путь в виде временного HTTP-сервера.

Message 913

Date: 03.03.2017 16:12:58

Похоже, DocumentStream по определению не переваривает ничего, кроме HTML, независимо от установленных расширений. Даже JPEG скормить ему не получается.

Есть два решения этой проблемы:

1. Создать свою реализацию интерфейса IInternetProtocol и раздавать PDF по нестандартной URL-схеме

2. Поднять свой сервер, и раздавать с него по обычному HTTP-протоколу.

Я думаю, что второй вариант проще, в прочем, о вкусах не спорят. Предлагую сделать так.

Добавьте в проект класс:

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Threading;

namespace WinFormsTest
{
    public class SERVER
    {
        const string prefix = "http://localhost:8080/server/";//URL, на котором будем раздавать HTTP
        const string MIME = "application/pdf";//тип документа

        byte[] bytes;//двоичные данные документа
        HttpListener listener;//объект, используемый для раздачи документа по HTTP
        Thread th;//объект серверного потока
        Object cs;//объект, используемый для синхронизации потоков

        public SERVER(byte[] source)
        {
            bytes = source;
            cs = new object();
        }

        void ThreadProc()
        {           
            HttpListenerContext context;
            // Note: The GetContext method blocks while waiting for a request.

            while (true)
            {

                context = listener.GetContext();

                lock (cs)
                {                    
                    // Obtain a response object.
                    HttpListenerResponse response = context.Response;

                    // Construct a response.   
                    response.ContentLength64 = bytes.Length;
                    response.ContentType = MIME;
                    System.IO.Stream output = response.OutputStream;
                    using (output)
                    {
                        output.Write(bytes, 0, bytes.Length);
                    }
                }
            }
        }

        public void Start()
        {
            string[] prefixes = new string[] { prefix };

            if (!HttpListener.IsSupported)
            {
                throw new ApplicationException("Windows XP SP2 or Server 2003 is required to use the HttpListener class.");
            }

            lock (cs)
            {
                // Create a listener.
                listener = new HttpListener();
                // Add the prefixes.
                foreach (string s in prefixes)
                {
                    listener.Prefixes.Add(s);
                }

                listener.Start();
            }

            th = new Thread(ThreadProc);
            th.IsBackground = true;
            th.Start();
        }

        public void Stop()
        {
            lock (cs)
            {
                listener.Stop();
                listener.Close();
                listener = null;
            }
            th.Abort(); 
            th = null;
        }

    }
}

Когда вам нужно отобразить документ, загрузите его в массив байт, запустите сервер, и направьте веб-браузер на него:

SERVER srv;
byte[] bytes;

private void button2_Click(object sender, EventArgs e)
{
            //поместить содержимое файла в поле bytes

            srv = new SERVER(bytes);
            srv.Start();
                        
            webBrowser1.Navigate("http://localhost:8080/server/");
}


Когда сервер больше не нужен, вызовите метод Stop.

Недостаток этого подхода в том, что задействованы ресурсы TCP. Реальные данные, конечно, в сеть не посылаются; но все же число соединений ограничено, что может привести к проблемам когда на машине есть другой софт, создающий много TCP-соединений. При использовании первого метода этого можно было бы исбежать.

      

Message 907

Date: 06.03.2017 14:09:02

Писать в файловую систему - тоже нормальный подход, но я не рекомендую привязываться к пути запуска приложения. Приложение может может быть запущено из каталога, на запись в который у пользователя нет прав (или вообще с CD-ROM диска, если на то пошло), тогда программа будет падать с бессмысленным сообщением об ошибке.

Лучше писать во временную папку пользователя, возвращаемую методом GetTempPath. И естественно, не забывать удалять файл, когда он больше не нужен.


Автор: VadimTagil

Главная страница - Список тем - Репозиторий на GitHub