跳到主要內容

【工具篇】在.Net中實現HTML生成圖片或PDF的幾種方式

前段時間由於項目上的需求,要在.Net平台下實現把HTML內容生成圖片或PDF文件的功能,特意在網上研究了幾種方案,這裏記錄一下以備日後再次使用。當時想着找一種開發部署都比較清爽並且運行穩定的方案,但實際上兩者同時滿足基本不可能,只能做一個自己覺得合適的取捨,下面從兩個維度(清爽指數和功能指數)逐一對比。




1.   WebBrowser


這種方案在開發時不依賴任務外部程序集和nuget包,部署時也不需要安裝額外的工具和服務,可以說是非常清爽了。它藉助了WinForm下的WebBrowser控件實現HTML內容渲染,並把渲染結果繪製在Bitmap中,進而保存成圖片或PDF文件。這種方案簡單粗暴,是C#中最基礎的實現方式,也是網上搜索結果最多的一種,下面看它的核心代碼(從網上拼湊得來):


 1     class WebBrowserPage2Image 
2 {
3 Bitmap m_Bitmap;
4
5 string m_Url;
6
7 public void Convert(string pageUrl, string fileName)
8 {
9 m_Url = pageUrl;
10 Thread m_thread = new Thread(new ThreadStart(HtmlDrawToBitmap));
11 m_thread.SetApartmentState(ApartmentState.STA);
12 m_thread.IsBackground = true;
13 m_thread.Start();
14 m_thread.Join();
15 MemoryStream stream = new MemoryStream();
16 m_Bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
17 byte[] buff = stream.ToArray();
18 FileStream fs = new FileStream(fileName, FileMode.Create);
19 stream.WriteTo(fs);
20 stream.Dispose();
21 stream.Close();
22 fs.Close();
23 }
24
25 private void HtmlDrawToBitmap()
26 {
27 WebBrowser browser = new WebBrowser();
28 browser.ScrollBarsEnabled = false;
29 browser.Navigate(m_Url);
30 browser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(delegate (object sender, WebBrowserDocumentCompletedEventArgs bdce)
31 {
32 if (browser.ReadyState == WebBrowserReadyState.Complete)
33 {
34 //myWebBrowser.Document.Body.Style = "zoom:180%";
35 Rectangle r = browser.Document.Body.ScrollRectangle;
36 browser.Height = r.Height;
37 browser.Width = r.Width;
38 m_Bitmap = new Bitmap(browser.Width, browser.Height);
39 browser.BringToFront();
40 browser.DrawToBitmap(m_Bitmap, new Rectangle() { Width = browser.Width, Height = browser.Height });
41 }
42 });
43 while (browser.ReadyState != WebBrowserReadyState.Complete)
44 {
45 Application.DoEvents();
46 }
47 browser.Dispose();
48 }
49 }

View Code

雖然開發起來非常簡潔,但是問題也很明顯。WebBrowser是Winform下的一個組件,在非Winform項目中運行會出現不可知的異常,即使在Winform項目中,數據量比較大的時候依然會出現卡死的情況。我做過500次循環的測試,在執行到100多次的時候程序出現假死不動也無異常拋出。除此之外,生成的圖片失真也比較嚴重,特殊字體和部分CSS樣式無法渲染。總的來說,基本無法達到生成環境需求。


清爽指數:★★★★★    功能指數:★




2.         Wkhtmltox


這也是網上廣泛流傳的一個方案,wkhtmltox是一套開源的命令行工具,提供了圖片和PDF的轉換能力,它採用C++編寫,使用Webkit作為渲染引擎,開源地址是https://github.com/wkhtmltopdf/wkhtmltopdf。使用方法就是在命令行工具中執行命令,例如:


wkhtmltopdf --grayscale  https://www.baidu.com  baidu.pdf

如果要在.Net項目中使用的話,核心問題就是用程序喚起命令行,同時指定參數執行即可,類似於下面的代碼:


       System.Diagnostics.ProcessStartInfo Info = new System.Diagnostics.ProcessStartInfo(); 
Info.FileName
= @"D:\dev\wkhtmltox\bin\wkhtmltopdf.exe";
Info.WindowStyle
= System.Diagnostics.ProcessWindowStyle.Hidden;
Info.CreateNoWindow
= true;
Info.Arguments
= @"-q --orientation Landscape https://www.baidu.com D:\\baidu.pdf";
System.Diagnostics.Process proc
= System.Diagnostics.Process.Start(Info);
proc.WaitForExit();
proc.Close();

更多強大的功能例如加水印、分頁、改樣式等可以參考這篇文章:https://www.cnblogs.com/82xb/p/7837597.html


詳細的參數說明可以查看文檔:https://wkhtmltopdf.org/usage/wkhtmltopdf.txt


GitHub上有很多針對各個開發語言的封裝,使用起來比較方便,唯一不爽的是部署項目前要先安裝好這個工具。


清爽指數:★★★★    功能指數:★★★★




3.         PuppeteerSharp


這個就更厲害了,說到這個就不得不先介紹下Puppeteer,因為PuppeteerSharp正是從Puppeteer衍生而來。


Puppeteer是由谷歌開源的一個Node項目,它提供了和Chrome DevTools的通信能力,基本上我們能在Chrome實現的操作通過它的API都可以實現,強大到讓你不敢相信。主要的應用有:



  • 生成頁面快照(圖片、PDF)

  • 爬蟲,網站內容抓取

  • 自動化測試(模擬鍵盤鼠標輸入,表單提交,UI測試等)

  • 網站性能分析(追蹤,時間線捕獲等)


開源地址是https://github.com/GoogleChrome/puppeteer


在Node項目中使用Puppeteer非常簡單,先安裝npm包:


npm i puppeteer

安裝過程可能會有點慢,因為在安裝的時候會下載一個最近版本的Chromium(Mac下大概170M,Linux下大概282M,Windows下大概280M)。當然,如果你本地已經有一個Chromium,可以設置npm的全局配置PUPPETEER_SKIP_CHROMIUM_DOWNLOAD 跳過下載,然後在程序中手動指定Chromium的位置。


生成圖片和PDF文件例子:


const puppeteer = require('puppeteer'); 

(async ()
=> {
const browser
= await puppeteer.launch();
const page
= await browser.newPage();
await page.goto(
'https://www.baidu.com');
await page.screenshot({path:
'baidu.png'});
await page.pdf({path:
'baidu.pdf', format: 'A4'});
await browser.close();
})();
 

Puppeteer默認使用無界面模式(headless:true),如果想看到完整的瀏覽器界面,可以通過下面的設置開啟:


  const browser = await puppeteer.launch({headless: false});

Puppeteer提供了豐富的選擇器接口,可以輕鬆實現模擬輸入和鼠標點擊,例如:


  await page.type('#index-kw', 'cnblogs'); 
await page.click(
'#index-bn');

      還支持指定使用設備:


  const devices = require('puppeteer/DeviceDescriptors'); 
await page.emulate(devices[
'iPhone 8']);

      詳細的API文檔可以參考:https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md


Puppeteer確實非常強大,但由於它是一個Node包無法直接在C#項目中使用,那怎麼辦呢?好在有國外的大神把Puppeteer移植到了.Net平台,也就是PuppeteerSharp。


注意:PuppeteerSharp是基於NetStandard 2.0開發的,所以項目的平台最低版本要是.NET Framework 4.6.1和.NET Core 2.0。


首先通過nuget安裝:


PM > Install-Package PuppeteerSharp

導入命名空間:


  using PuppeteerSharp;

下面是我在ASP.NET Core 2.1下封裝的測試方法:


        [HttpPost, Route("page2img")] 
public async Task<string> PageToImage(string url, int? width, int? height)
{
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless
= true,
//ExecutablePath="",
Args = new string[] { "--no-sandbox" }
});
var page = await browser.NewPageAsync();
bool fullPage = true;
if (width.HasValue && height.HasValue)
{
await page.SetViewportAsync(new ViewPortOptions
{
Width
= width.Value,
Height
= height.Value
});
fullPage
= false;
}
await page.GoToAsync(System.Web.HttpUtility.UrlDecode(url));
string fileName = $"/Files/{Guid.NewGuid().ToString()}.png";
await page.ScreenshotAsync($"{AppDomain.CurrentDomain.BaseDirectory}{fileName}", new ScreenshotOptions { FullPage = fullPage });
return $"{Request.Host.ToString()}{fileName}";
}

上面方法的第一行:


  await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);

程序會判斷本地環境有沒有可用的Chromium,如果沒有的話會自動下載一個默認版本的Chromium,這個過程可能會有點長,下載成功後會在項目根目錄多一個這樣的文件夾:



和前面說的一樣,如果本地已經下載過Chromium,可以通過LaunchOptionsExecutablePath字段指定一個目錄。目前PuppeteerSharp在網上的資料還不是很多,但是得益於它與Puppeteer高度完整和相似的API,Puppeteer的文檔對它基本都能適用。


總體來說,這個工具功能強大並且比較穩定(我在Windows和Linux下都測試通過),是一個不錯的選擇,但是由於它必須依賴於Chromium來運行,打包部署並不是很方便,我建議把它作為一個獨立的web服務。


清爽指數:★★★    功能指數:★★★★★




4.         IronPdf


    除了一些開源的項目和工具能提供HTML轉圖片或PDF的功能,很多商業軟件公司也提供了這樣的產品,IronPdf算是裏面比較有代表性的一個。和其他收費軟件不同的是,IronPdf有一個對開發者免費試用的license:



    IronPdf的主要特性包括:



  • 任何類型的HTML文件、代碼片段、URL生成PDF

  • PDF編輯

  • 圖片與PDF互轉

  • 支持HTML5和CSS3,支持響應式布局,支持JS腳本,豐富的配置選項

  • 支持C#、VB、Webform、ASP.NET MVC、.NET CORE


    我們可以在官網下載DLL文件直接引用到項目,也可以通過nuget來安裝:


PM > Install-Package IronPdf

    導入命名空間:


  using IronPdf;

    一個最簡單的例子:


// Create a PDF from any existing web page 
var Renderer = new IronPdf.HtmlToPdf();
Renderer.PrintOptions.EnableJavaScript
= true;
Renderer.PrintOptions.PaperOrientation
= IronPdf.PdfPrintOptions.PdfPaperOrientation.Landscape;
var PDF = Renderer.RenderUrlAsPdf("https://www.baidu.com");
PDF.SaveAs(
"baidu.pdf");

// This neat trick opens our PDF file so we can see the result
System.Diagnostics.Process.Start("baidu.pdf");

    添加水印:


  pdf.WatermarkAllPages("<h2 style='color:red'>SAMPLE</h2>", PdfDocument.WaterMarkLocation.MiddleCenter, 50, -45, "https://www.baidu.com");

    用圖片生成PDF文檔:


// Select one or more images.  This example selects all JPEG images in a specific folder. 
var ImageFiles = Directory.EnumerateFiles(@"C:\project\assets").Where(f => f.EndsWith(".jpg") || f.EndsWith(".jpeg"));

// Convert the images to a PDF and save it.
ImageToPdfConverter.ImageToPdf(ImageFiles).SaveAs(@"C:\project\composite.pdf");

    更多高級功能和配置可以參考官網例子:https://ironpdf.com/examples/image-to-pdf/


 清爽指數:★★★★    功能指數:★★★★


    


寫在最後


    以上幾種方式,都是我在本次實踐中總結出來的,可能不是很全面,歡迎大家不吝補充。


    遺憾的是,最終項目沒有用上面的任何一種方式,而是抓取到HTML內容後用正則解析,然後用Bitmap一點一點重新畫圖生成圖片文件保存。因為我要截取的頁面內容很少,就是一個簡單的电子處方箋,需求上也沒有要求必須完全和原網頁100%一致,繪圖也算是一個不錯的方案,但是缺點是一旦HTML結構或樣式發生變化,那這套東西就失效了,好在這個不會輕易變更,也算是一個折中方案。


 


 


 

【精選推薦文章】



智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選



想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計



帶您來看台北網站建置台北網頁設計,各種案例分享



廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益



Orignal From: 【工具篇】在.Net中實現HTML生成圖片或PDF的幾種方式

留言

這個網誌中的熱門文章

強強聯手!攜手打造雲林縣Web3.0 領地方品牌進軍元宇宙

中華電信攜手國內最大Potato Media Web3.0社群共享平台,及品牌醫生果俐文創三方結合,透過經濟部「CBMP智慧雲遊跨域串連計畫」,協助品牌數位轉型,帶動品牌體驗情境、新商業模組升級與行動支付應用,第一站選在雲林縣當地原生消費品牌,打造社群消費循環,探索近期最夯的元宇宙新玩法。 Potato Media執行長顏宏霖表示,在CBMP計畫協助下,加上站內Web3.0資源,將快速協助雲林縣當地品牌升級轉型,幫助店家創造品牌價值,包含黑矸仔醬油、四代目麥芽酥、玉津烘焙坊、禪屋米胖工坊、YuDS沐耳飲、莫蒂精品巧克力、維野納複合式餐飲、頂雲咖啡、媽祖埔豆腐張、玉山碾米廠等,另外嘉義市麥麵、名香茗茶也等不及跨縣市加入,結合Potato Media站內活動,打造雲林專屬限定NFT道具與頭框,發行雲林扭蛋,體驗全新元宇宙新世界。 左起雲林縣計畫處處長李明岳、果俐文創執行長-陳郁涵、雲林縣議員周秀月、中華電信雲林營運處總經理張肇家、Potato Media執行長顏宏霖、LINE禮物代表睿鼎數位、12CMTaiwan行銷總監楊涵柔。(圖/由Potato Media提供) 此次Potato Media平台合作內容是店家會員註冊活動:首次註冊可得100積分 + 一顆扭蛋,店家推薦文章介紹,可領取店家消費優惠券,站內扭蛋禮物抽獎活動,獎項豐富:雲林限定禮品、雲林意象限定NFT道具與頭框,雲林幣扭一下APP政令大聲公獎勵活動,另外各種雲林美食伴手禮、住宿旅遊的店家,都可以使用行動支付或上LINE禮物平台實際體驗消費。 推薦評價好的 iphone維修 中心 擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢 產品缺大量曝光嗎?你需要的是一流 包裝設計 窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。 雲林縣副縣長謝淑亞強調,此次計畫和Potato Media 合作,Potato Media 是一個在 2021 年 4 月正式發布的「區塊鏈 Web3.0 共享社群平台」,創作者和使用者都可以透過互動的機制,例如對文章點讚、留言與轉發分享,來獲取相對應比例的加密貨幣CFO(Potato Media 平台上的原生加密貨幣),跟Facebook、Instagram、YouTu...

要上網站行銷農產品,得經過市政府抽驗農藥殘留,檢驗合格才能上網行銷

台北網站設計      網頁設計公司     網站設計公司 食安五環為「源頭控管」、「重建生產管理履歷」、「提高查驗能力」、「加重生產者、廠商的責任」及「鼓勵、創造監督平臺」五大要環,除推動政府與市民消費者共同監督食品安全,更要從農場到餐桌,鏈結農村產業線,以網路社群為媒介,建立嘉義市農村網頁平台。活動現場介紹「農抵嘉」產銷網頁平台的內容,包含在地人文地產景,將景觀、產業、生態及文化等資訊整合、展現,更推廣在地農村農友優質、安全的農產品。要上「農抵嘉」網站行銷農產品,得經過市政府抽驗農藥殘留,檢驗合格才能上網行銷自己的農產品,嘉義市農友可以多利用,也提供店家採購管道。 「尚安心,農抵嘉」,透過「農抵嘉」產銷網頁平台,鏈結農村產業線並促進地產地消,從源頭管理食安,讓政府管理、民間參與,透過食安小教育,讓小朋友接觸農作物,與生產者互動,產生共鳴,一同攜手維護嘉義市的食安。記者會中也展示紅瓦厝社區所做的紅瓦窯烤,並有其他下埤花生等農產品,透過農場職人使「農特產品不只是農產品」,民眾可登入「農抵嘉」網頁http://chiayirural.com/index.asp瞭解更進一步的訊息。 網動廣告 參考資料:蕃新聞https://n.yam.com/Article/20171226529712 Orignal From: 要上網站行銷農產品,得經過市政府抽驗農藥殘留,檢驗合格才能上網行銷

IEA:疫情衝擊能源需求 但再生能源呈創紀錄成長_台中搬家公司

※ 台中搬家公司 教你幾個打包小技巧,輕鬆整理裝箱! 還在煩惱搬家費用要多少哪?台中大展搬家線上試算搬家費用,從此不再擔心「物品怎麼計費」、「多少車才能裝完」 摘錄自2020年11月10日中央社報導 國際能源總署(IEA)報告今天(10日)指出,武漢肺炎(COVID-19)疫情可能衝擊能源需求,但電力部門再生能源繼續以創紀錄速度成長。 IEA執行董事比羅爾(Fatih Birol)表示:「在2025年,再生能源將成為全球最大發電來源,預計將提供1/3的全球電力,終結煤炭50年來作為最大電力供應來源的地位。」 ※推薦 台中搬家公司 優質服務,可到府估價 台中搬鋼琴,台中金庫搬運,中部廢棄物處理,南投縣搬家公司,好幫手搬家,西屯區搬家 IEA關於再生能源的年度報告估計,儘管受到疫情干擾,今年的再生能源新發電容量可望創紀錄,達將近200GW(GW為十億瓦)。 能源轉型 國際新聞 再生能源 疫情看氣候與能源 本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理 ※ 台中搬家公司 教你幾個打包小技巧,輕鬆整理裝箱! 還在煩惱搬家費用要多少哪?台中大展搬家線上試算搬家費用,從此不再擔心「物品怎麼計費」、「多少車才能裝完」 Orignal From: IEA:疫情衝擊能源需求 但再生能源呈創紀錄成長_台中搬家公司