整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          24-跨平臺導PDF,結合wkhtmltopdf很順手

          好東西要分享,之前一直在使用wkhtmltopdf進行pdf文件的生成,常用的方式就是先安裝wkhtmltopdf,然后在程序中用命令的方式將對應的html生成pdf文件,簡單而且方便;但重復的編碼使得想在wkhtmltopdf基礎上進行封裝,偶然間發現有小伙伴已經封裝的還不錯啦,常用的功能都已經實現,源碼地址:https://github.com/fpanaccia/Wkhtmltopdf.NetCore。

          作者將其打包成Nuget包(Wkhtmltopdf.NetCore),直接引入使用即可;

          正文

          既然用到了.NetCore,肯定就要考慮到跨平臺兼容性,對于wkhtmltopdf之前一直是在Windows上使用,還沒有在其他平臺嘗試;這個包封裝的行不行,拉出來遛遛就知道啦,接下來就試試:

          1. 建個API項目,引入包和兼容對應平臺的wkhtmltopdf執行文件



          注: 默認依賴的wkhtmltopdf執行文件需要存放在Rotativa目錄下,可以自定義名稱,如果自定義,需要再注冊服務時指定對應的文件名;這里的wkhtmltopdf已經根據不同平臺進行編譯打包了,無需安裝,這些文件在源碼那就有;

          2.創建PDFTestController控制器,添加如下接口進行測試

          首先把生成pdf的服務注入進來,后續直接使用就可以啦:



          接下來就開始寫接口啦,這里只是測試,代碼冗余沒有考慮,在實際項目中小伙伴可以根據自己需求進行封裝;

          • ExportPDFByHtml 接口,用html直接生成pdf文件,但這里沒有保存,以文件流的形式訪問,通過瀏覽器查看文件,可以自行下載;html模板在實際開發過程中可以單獨用文件存儲;



          • SavePDFByHtml接口,直接保存文件,文件名可以根據需要進行自定義;



          • TestMarginAndPageSize接口,設置Margin和PageSize參數,其他參數也可以設置;



          ConvertOptions默認封裝了以下屬性,小伙伴也可以自定義擴展,只要繼承IConvertOptions即可,這里就不演示的,因為官方有對應的案例,下伙伴下去搞搞,wkhtmltopdf的參數挺多的,都可以進行封裝使用。



          • ExportByRazorView使用Razor視圖的方式進行pdf文件生成,此庫已經支持cshtml文件的讀取



          根據指定視圖生成對應的pdf效果,如下:



          • ExportByRazorViewData數據動態綁定,既然支持視圖,那就應該支持Razor語法,一般常用的就是數據綁定了,上面是靜態的,接下來來個動態綁定的。



          根據指定視圖生成對應的pdf效果,如下:



          如上基本的使用演示就說那么多,使用還是很簡單,小伙伴后續可以根據自己的需要進行相關擴展;當然還有其他功能,比如設置頁眉/頁腳等,作者提供有對應的案例;這里不說那么多,不然又是長文。

          3. 小伙伴用的時候可能會遇到的問題

          • 在開發調試運行項目時,會報找不到wkhtmltopdf文件,那是因為運行時的確找不到對應的文件,將對應Rotativa下的文件設置為始終復制即可:



          • 在Windows下怎么玩都沒問題啦,開始發布到Linux(我用的centos 7),我擦,莫名其妙的錯。



          看見這個錯我懵的,一頓搜索猛如虎,還是沒找到答案;冷靜下來,重新捋捋,原來是自己在犯傻;

          兩個問題需要解決,1.上傳到Linux下的wkhtmltopdf沒有給執行權限;2.可能環境缺少對應的依賴庫;

          設置可執行權限

          在Linux環境下,可以通過ll命令查看權限,剛開始是沒有權限的,只需要執行chmod 777 wkhtmltopdf命令,執行權限就有了,如下圖中紅框中的x就是可執行權限;關于Linux常用命令后續單獨整理一篇分享吧,這里先不延伸。



          安裝缺少的依賴庫

          可執行權限開啟之后,別急著去訪問頁面,這樣可能還是錯誤。因為可能缺少依賴庫,那咋知道缺少呢,我是直接執行wkhtmltopdf,執行成功就沒啥,不成功就會報缺少相關依賴,然后直接安裝就行啦;執行./wkhtmltopdf https://www.baidu.com ./test.pdf試試就知道啦,因為wkhtmltopdf本身是可以單獨運行的,并不依賴我們寫的程序。

          • 當執行成功之后,然后開始訪問接口導出功能,如果不出意外,遇到中文就產生亂碼啦,那是因為Linux環境下缺少相關的字體文件,將對應的字體文件拷貝到Linux上即可,字體我找好了,下載地址如下:

          鏈接: https://pan.baidu.com/s/1jikC0DUkpEzpXL5ysjEQPA 提取碼: tn4j

          將下載下來的字體解壓,然后拷貝到Linux下的 /usr/share/fonts目錄下即可

          最后這樣應該就沒啥問題啦,剩下的就交給小伙伴自己摸索搞實踐吧;

          此文源碼地址:https://github.com/zyq025/DotNetCoreStudyDemo

          wkhtmltopdf官網地址:https://wkhtmltopdf.org/

          總結

          使用還是很簡單的,常規的需求沒啥問題,如果需要功能定制化,小伙伴可以參考源碼,自己封裝一個(封裝思路不難的); 如果小伙伴有比較好的導出庫,免費開源的那種,一起分享出來玩玩。

          感謝小伙伴的:點贊收藏評論,下期繼續~~~

          一個被程序搞丑的帥小伙,關注"Code綜藝圈",跟我一起學~~~

          用 Go 模板生成 HTML 頁面的示例

          在這里,我提供了另一個我為了跟進我之前關于“如何在 Golang 中使用模板”的文章而制作的例子。我將使用該文章中關于使用 Golang(Go 語言)模板的一些基本技巧。此外,對于這個項目,我還決定使用嵌套模板在模板中編寫我自己的簡單函數

          網頁是響應式的,所以它可以適應不同的屏幕寬度,當你點擊其中一個“產品”時,你會得到一個彈出窗口,里面有描述和一些按鈕。

          在原始示例中,產品和它們的描述都在 HTML 文件中硬編碼。使用模板并用(不同的)數據渲染它們可以避免硬編碼產品及其描述。

          接下來,我將首先向你展示實際的程序,它將解析和渲染模板,然后輸出生成的 HTML 文件。之后,我將向你展示不同模板的樣子,并討論我在它們中使用的一些技巧。

          實際的 Golang 程序

          這是會完成所有工作的 Golang 程序:

          package main
          
          import (
              "bufio"
              "bytes"
              "html/template"
              "os"
          )
          
          type product struct {
              Img         string
              Name        string
              Price       string
              Stars       float64
              Reviews     int
              Description string
          }
          
          func subtr(a, b float64) float64 {
              return a - b
          }
          
          func list(e ...float64) []float64 {
              return e
          }
          
          func main() {
          
              data := []product{
                  {"images/1.png", "strawberries", "$2.00", 4.0, 251, "Lorem ipsum dolor sit amet, consectetur adipiscing elit."},
                  {"images/2.png", "onions", "$2.80", 5.0, 123, "Morbi sit amet erat vitae purus consequat vehicula nec sit amet purus."},
                  {"images/3.png", "tomatoes", "$3.10", 4.5, 235, "Curabitur tristique odio et nibh auctor, ut sollicitudin justo condimentum."},
                  {"images/4.png", "courgette", "$1.20", 4.0, 251, "Phasellus at leo a purus consequat ornare ac aliquam quam."},
                  {"images/5.png", "broccoli", "$3.80", 3.5, 123, "Maecenas sed ante sagittis, dapibus dui quis, hendrerit orci."},
                  {"images/6.png", "potatoes", "$3.00", 2.5, 235, "Vivamus malesuada est et tellus porta, vel consectetur orci dapibus."},
              }
          
              allFiles := []string{"content.tmpl", "footer.tmpl", "header.tmpl", "page.tmpl"}
          
              var allPaths []string
              for _, tmpl := range allFiles {
                  allPaths = append(allPaths, "./templates/"+tmpl)
              }
          
              templates := template.Must(template.New("").Funcs(template.FuncMap{"subtr": subtr, "list": list}).ParseFiles(allPaths...))
          
              var processed bytes.Buffer
              templates.ExecuteTemplate(&processed, "page", data)
          
              outputPath := "./static/index.html"
              f, _ := os.Create(outputPath)
              w := bufio.NewWriter(f)
              w.WriteString(string(processed.Bytes()))
              w.Flush()
          }

          在第3-8行,我們導入了必要的包。包bufiobytesos用于輸出 HTML 文件。包html/template用于處理模板。

          在第10-17行,我們定義了product,這是一個用于存放產品數據的struct。確保所有的屬性都是大寫的,也就是說,它們都是可導出的。在 Golang 模板中使用 struct 時,這是必需的。

          另外,請注意星級評分是一個在0到5之間的浮點數。我們將這個數字的類型定義為float64,是因為模板中所有的浮點數都使用float64

          在第19-25行,我創建了兩個函數,因為我對sprig包提供的函數并不滿意。第一個函數叫做subtr(),是一個簡單的浮點數減法函數,第二個函數叫做list(),用于創建一個浮點數列表。

          在第29-36行,我們定義了要在模板中渲染的數據。在這個例子中,數據是硬編碼的。在更現實的環境中,這些數據可能會存儲在某種類型的數據庫中。注意,數據遵循了product結構的定義。

          在第38行,我們將所有模板的文件名添加到一個切片中。

          在第40-43行,將目錄名/templates/添加到所有文件名中。

          在第45行,我們使用ParseFiles()解析模板,確保使用template.FuncMap{}添加了兩個函數subtrlist,并使用template.Must()檢查模板的正確性。

          在第47-48行,我們將渲染的模板輸出到一個字節緩沖區。為了確保渲染器以page模板開始,我們使用ExecuteTemplates()并將"page"作為參數。在這里,我們也將我們定義的數據提供給渲染器。

          在第50-54行,渲染的字節緩沖區輸出到./static目錄中名為index.html的 HTML 文件。

          生成 HTML 的模板

          在這里我將討論本項目使用的所有模板。

          “page”模板

          這是定義完整網頁的模板。它調用了其他三個模板:headercontentfooter

          {{define "page"}}
          {{template "header" -}}
          {{template "content" . -}}
          {{template "footer" -}}
          {{end}}

          在第1行,必須在此模板內定義page,否則ExecuteTemplates()將找不到它。

          在第3行,注意,在調用content模板時添加了一個點。這向模板渲染器發出信號,表示提供的數據要發送到content模板。這與向函數提供參數一樣。鑒于headerfooter模板不需要數據,因此也無需添加點。

          “header”模板

          這個模板用于渲染一個典型的HTML頭部,加載字體、樣式和JavaScript代碼。

          {{define "header" -}}
          <!DOCTYPE html>
          <html lang="en">
          <head>
             <meta charset="UTF-8">
             <meta http-equiv="X-UA-Compatible" content="IE=edge">
             <meta name="viewport" content="width=device-width, initial-scale=1.0">
             <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
             <link rel="stylesheet" href="css/style.css">
             <script src="js/script.js" defer></script>
          </head>
          <body>
          {{end}}

          注意,我們使用defineend關鍵字來定義這個模板為header模板。

          另外,注意雙結束括號前的‘-’符號。這個符號告訴渲染器移除空白和換行符。根據我的經驗,你需要嘗試這些‘-’的放置位置。

          為了達到期望的輸出,你有時需要將‘-’放在開始的雙括號之后,有時需要放在結束的雙括號之前,有時兩個地方都需要放。

          “content”模板

          這是渲染HTML文件主體的模板。

          將內容與頭部和尾部分開定義的一個原因是,我們可以快速創建其他頁面,這些頁面具有不同的內容,但頭部和尾部是相同的——也就是說,加載相同的CSS,JS,字體等。

          如前所述,我創建了自己的Spriglistsubf函數版本,因為我對該包中的函數不滿意。這是因為我遇到了一些無法解決的錯誤。我還決定將我的減法函數命名為subtr而不是subf

          {{define "content" -}}
          <div class="container">
             <h3 class="title"> organic products </h3>
             <div class="products-container">
          {{range $index, $item :=.}}
                <div class="product" data-name="p-{{$index}}">
                   <img src="{{$item.Img}}" alt="">
                   <h3>{{$item.Name}}</h3>
                   <div class="price">{{$item.Price}}</div>
                </div>
          {{end}}
             </div>
          </div>
          
          <div class="products-preview">
          {{range $index, $item:=.}}
            <div class="preview" data-target="p-{{$index}}">
                <i class="fas fa-times"></i>
                <img src="{{$item.Img}}" alt="">
                <h3>organic {{$item.Name}}</h3>
                <div class="stars">
                {{- $stars := list 1.0 2.0 3.0 4.0 5.0 -}}
                {{- range $stars}}
                      {{$x:= subtr . 0.5}}
                      {{- if lt $item.Stars $x -}}
                      <i class="far fa-star"></i>
                      {{- else if lt $item.Stars . -}}
                      <i class="fas fa-star-half-alt"></i>
                      {{- else -}}
                      <i class="fas fa-star"></i>
                      {{- end -}}
                {{end}}
                   <span>( {{$item.Reviews}} )</span>
                </div>
                <p>{{$item.Description}}</p>
                <div class="price">{{$item.Price}}</div>
                <div class="buttons">
                   <a href="#" class="buy">buy now</a>
                   <a href="#" class="cart">add to cart</a>
                </div>
             </div>
          {{end}}
          </div>
          {{end}}

          在第5到12行,我們遍歷數據中的所有產品。對于每個產品,我們創建內部模板變量$index$item。我們使用$index作為data-name屬性。從$item中我們提取ImgName,和Price屬性用于HTML。

          在第16到42行,我們做的和上面一樣,但我們也從$item中提取$Stars $Description$Reviews屬性。

          此外,在第22行,我們創建了一個名為$stars的列表。這個列表包含從1.0到5.0的浮點數。我們需要這些數來顯示星星。這些數表示顯示每個星星的閾值。

          然后,在第23到32行,我們創建一個循環來顯示星星。基本上,我們有三種類型的星星可以顯示:滿星,半星,和空星。

          使用條件語句,我們確定一個項目的星星數($item.Stars)是否小于$stars列表中的星星數減去0.5,或小于星星數,或更高。在第一種情況下,我們顯示一個空星。在第二種情況下,我們顯示一個半星。最后,在最后一種情況下,我們顯示一個滿星。

          “footer”模板

          除了將模板定義為footer之外,這里沒有特別的事情。

          {{define "footer" -}}
          </body>
          </html>
          {{end}}

          運行HTML生成器并查看結果

          確保main.go文件位于一個也包含兩個名為templatesstatic的子目錄的目錄中。上述模板都需要在templates目錄中。原始代碼中的CSS,JS和圖片需要在static目錄中的各自子目錄中。

          只需在終端的正確目錄中鍵入go run main.go來運行生成器。隨后,HTML文件將被生成并寫入static目錄作為index.html

          現在,你可以在瀏覽器中打開index.html文件,你應該得到頁面展示的結果。

          列表清單

          請記住,始終保持學習的態度,并享受編碼的樂趣!祝您編碼愉快!

          如果你喜歡我的文章,點贊,關注,轉發!

          家好,我是DD,已經是封閉在家的第51天了!

          最近一直在更新Java新特性(https://www.didispace.com/java-features/)和IDEA Tips(https://www.didispace.com/idea-tips/)兩個原創專欄,其他方向內容的動態關注少了。昨天天晚上刷推的時候,瞄到了這個神奇的東西,覺得挺cool的,拿出來分享下:

          相信你看到圖,不用我說,你也猜到是啥了吧?html里可以跑python代碼了

          看到好多Python公眾號已經開始猛吹未來了,但乍看怎么覺得有點像JSP?或者一些模版引擎?是進步還是倒退呢?與其瞎想,不如仔細看看這個東東的能力吧!

          根據官方介紹,這個名為PyScript的框架,其核心目標是為開發者提供在標準HTML中嵌入Python代碼的能力,使用 Python調用JavaScript函數庫,并以此實現利用Python創建Web應用的功能。

          看到介紹里提到了調用JavaScript函數庫的能力,看來跟JSP或者模版引擎還是有區別的。

          PyScript 快速體驗

          官方給了一個例子,可以幫助我們觀的感受這個開發框架的能力,不妨跟著DD看看,它能做啥吧!

          第一個案例,hello world

          代碼很簡單,就下面這幾行。你只需要創建一個html文件,然后復制進去就可以了。

          <html>
            <head>
              <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
              <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
            </head>
            <body> 
              <py-script> 
                  print('Hello, World!') 
              </py-script> 
            </body>
          </html>
          

          保存好之后,在瀏覽器里打開就能看到這樣的頁面了:

          回頭再看看這個html里的內容,三個核心內容:

          • 引入pyscript的樣式文件:<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
          • 引入pyscript的腳本文件:<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
          • <py-script>標簽中寫具體的python代碼來輸出Hello World

          如果你懶得自己敲代碼的話,本文的兩個案例代碼我打包放在公眾號了,需要的朋友可以關注公眾號“程序猿DD”,回復:pyscript 獲取。

          第二個案例,數據定義 + 數據展示

          先創建一個data.py文件,然后加入前面的代碼。功能很簡單,就是隨機生成(x,y)的坐標

          import numpy as np
          
          def make_x_and_y(n):
              x = np.random.randn(n)
              y = np.random.randn(n)
              return x, y
          

          再創建一個html文件,加入下面的代碼

          <html>
              <head>
                <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
                <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
                <py-env>
                  - numpy
                  - matplotlib
                  - paths:
                    - /data.py
                </py-env>
              </head>
          
            <body>
              <h1>Let's plot random numbers</h1>
              <div id="plot"></div>
              <py-script output="plot">
              import matplotlib.pyplot as plt
              from data import make_x_and_y
          
              x, y = make_x_and_y(n=1000)
          
              fig, ax = plt.subplots()
              ax.scatter(x, y)
              fig
              </py-script>
            </body>
          </html>
          

          這里就稍微復雜一些了,除了hello world中的幾個要點外,這里還有這幾個要關注的地方:

          • <py-env>標簽:這里聲明要引入的包和要引入的文件(上面創建的data.py
          • <py-script output="plot">:這里定義了要在<div id="plot"></div>中輸出的內容,可以看到這里的邏輯都是用python寫的

          這個頁面的執行效果是這樣的:

          是不是很神奇呢?整個過程中都沒有大家熟悉的cs、js內容,就完成了這樣一個圖的頁面實現。

          小結

          最后,談談在整個嘗試過程中,給我的幾個感受:

          1. 開發體驗上高度統一,對于python開發者來說,開發Web應用的門檻可以更低了
          2. 感覺性能上似乎有所不足,幾個復雜的案例執行有點慢,開始以為是部分國外cdn的緣故,后來移到本地后,還是慢。這部分可能還需要進一步優化。

          這個開發框架目前還只是alpha版本,未來一定還會有更多特性與優化出來,總體上我覺得這個框架還是非常cool的,尤其對于剛學會Python,或者只會Python,但又想快速開發Web應用的小伙伴來說,可能將會是個不錯的選擇,那你覺得這個框架如何?未來會不會火?留言區聊聊吧!


          主站蜘蛛池模板: 国产一区二区视频免费| 人妻体内射精一区二区三四| 夜色阁亚洲一区二区三区 | 精品国产免费一区二区三区香蕉| 99精品国产高清一区二区| 亚洲高清毛片一区二区| 中文字幕一区二区三区5566| 日本无卡码免费一区二区三区| 国产精品视频一区| 亚洲熟女少妇一区二区| 无码精品不卡一区二区三区| 成人无码一区二区三区| 日韩av片无码一区二区不卡电影| 国产激情无码一区二区三区| 国产手机精品一区二区| 一区二区免费在线观看| 欧美成人aaa片一区国产精品| 动漫精品第一区二区三区| 蜜臀AV免费一区二区三区| 一区二区高清视频在线观看| 精品无码一区二区三区爱欲九九| 影院无码人妻精品一区二区| 亚洲熟女www一区二区三区| 国产精品丝袜一区二区三区| 日韩人妻精品无码一区二区三区 | 日韩精品人妻一区二区中文八零 | 久久精品无码一区二区三区日韩| 国产肥熟女视频一区二区三区| 制服中文字幕一区二区 | 午夜天堂一区人妻| 亚洲视频一区二区三区| 一区二区三区视频在线| 精品国产福利一区二区| 久久精品动漫一区二区三区| 视频一区在线播放| 美女视频免费看一区二区| 国产精品成人一区二区三区| 日本香蕉一区二区三区| 美女AV一区二区三区| 亚洲一区二区三区在线视频| 国产精品无码一区二区在线观一|