Date: 02.03.2017 13:34:51
Во-первых, чтобы вообще отобразить PDF-файл в WebBrowser, необходимо наличие на целевой машине соответствующего расширения Internet Explorer, работоспособного в используемой версии (по умолчанию, компонент webBrowser использует более старую версию IE чем установленная на машине). Иначе IE просто предложит пользователю скачать файл.
Во-вторых, отобразить документ из базы данных в WebBrowser, конечно, можно. Для этого нужно унаследовать свой класс от Stream, реализовав логику загрузки из БД, и присвоить объект этого класса свойству WebBrowser.DocumentStream
В-третьих, как документ будет загружаться - поточно или целиком в память - полностью зависит от используемого для данного документа расширения IE. Сам WebBrowser ничего не знает о PDF.
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.
Date: 03.03.2017 8:12:39
Вы понимаете, что я свое первое сообщение не просто так писал?
Напишите, какое расширение IE установлено для просмотра PDF.
Date: 03.03.2017 13:00:49
Угу. Я подумаю, как решить эту проблему.
Скорее всего, придется использовать обходной путь в виде временного HTTP-сервера.
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-соединений. При использовании первого метода этого можно было бы исбежать.
Date: 06.03.2017 14:09:02
Писать в файловую систему - тоже нормальный подход, но я не рекомендую привязываться к пути запуска приложения. Приложение может может быть запущено из каталога, на запись в который у пользователя нет прав (или вообще с CD-ROM диска, если на то пошло), тогда программа будет падать с бессмысленным сообщением об ошибке.
Лучше писать во временную папку пользователя, возвращаемую методом GetTempPath. И естественно, не забывать удалять файл, когда он больше не нужен.
Автор: VadimTagil