最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于golang編寫一個word/excel/ppt轉(zhuǎn)pdf的工具

 更新時間:2024年11月15日 09:49:21   作者:翻斗  
這篇文章主要為大家詳細介紹了如何基于golang編寫一個word/excel/ppt轉(zhuǎn)pdf的工具,文中的示例代碼講解詳細,感興趣的小伙伴可以了解下

需求

公司客戶有需求,需要轉(zhuǎn)換doc文件為pdf文件,并且保持格式完全不變。

工程師用各種Java類庫,無論是doc4j、POI還是Aspose.Doc、Libreoffice組件還是各種線上API服務(wù),轉(zhuǎn)換結(jié)果都不甚滿意。

于是我這邊接手這個活了。

調(diào)研

其實,最符合客戶需求的莫過于原生Windows Office Word的導出功能了。

需要能夠操作Windows的Office Word程序,那么需要能夠直接訪問其系統(tǒng)組件,需要類似COM/OLE系統(tǒng)庫,說干就干。

1、運維做弄了一個配置比較低的EC2機器,windows10系統(tǒng)。

2、我這邊找了一些庫,python的comtypes.client,但是有點問題,單跑沒問題,做成服務(wù),在web線程中做這個事情,就有問題,具體找了下,應(yīng)該還是線程問題,想了想,不做了(因為本身就不想用python寫, )

3、趕緊找了下golang中對應(yīng)的OLE庫,找到了一個,看了下文檔,直接寫了出來。

實現(xiàn)

話不多說,直接上核心代碼看看:

下面是基礎(chǔ)的解析過程,其實就是模擬以下四個步驟:

1、打開Office對應(yīng)的程序(Word/Excel/PPT)

2、導出為PDF文件

3、關(guān)閉文件

4、退出Office程序

基礎(chǔ)邏輯

package office

import (
	ole "github.com/go-ole/go-ole"
	"github.com/go-ole/go-ole/oleutil"
	log "github.com/sirupsen/logrus"
)

/// 更多內(nèi)容請參考官方COM文檔 https://docs.microsoft.com/zh-cn/office/vba/api/word.application
type Operation struct {
	OpType    string
	Arguments []interface{}
}

/// 部分應(yīng)用不允許隱藏 ,比如ppt,所以Visible需要設(shè)定下
type ConvertHandler struct {
	FileInPath      string
	FileOutPath     string
	ApplicationName string
	WorkspaceName   string
	Visible         bool
	DisplayAlerts   int
	OpenFileOp      Operation
	ExportOp        Operation
	CloseOp         Operation
	QuitOp          Operation
}

type DomConvertObject struct {
	Application *ole.IDispatch
	Workspace   *ole.IDispatch
	SingleFile  *ole.IDispatch
}

func (handler ConvertHandler) Convert() {
	ole.CoInitialize(0)
	defer ole.CoUninitialize()

	log.Println("handle open start")
	dom := handler.Open()
	log.Println("handle open end")
	log.Println("handler in file path is " + handler.FileInPath)
	log.Println("handler out file path is " + handler.FileOutPath)

	defer dom.Application.Release()
	defer dom.Workspace.Release()
	defer dom.SingleFile.Release()

	handler.Export(dom)
	log.Println("handle export end")

	handler.Close(dom)
	log.Println("handle close end")

	handler.Quit(dom)
	log.Println("handle quit end")

}
func (handler ConvertHandler) Open() DomConvertObject {
	var dom DomConvertObject
	unknown, err := oleutil.CreateObject(handler.ApplicationName)
	if err != nil {
		panic(err)
	}
	dom.Application = unknown.MustQueryInterface(ole.IID_IDispatch)

	oleutil.MustPutProperty(dom.Application, "Visible", handler.Visible)
	oleutil.MustPutProperty(dom.Application, "DisplayAlerts", handler.DisplayAlerts)

	dom.Workspace = oleutil.MustGetProperty(dom.Application, handler.WorkspaceName).ToIDispatch()

	dom.SingleFile = oleutil.MustCallMethod(dom.Workspace, handler.OpenFileOp.OpType, handler.OpenFileOp.Arguments...).ToIDispatch()
	return dom
}

func (handler ConvertHandler) Export(dom DomConvertObject) {
	oleutil.MustCallMethod(dom.SingleFile, handler.ExportOp.OpType, handler.ExportOp.Arguments...)

}

func (handler ConvertHandler) Close(dom DomConvertObject) {
	if handler.ApplicationName == "PowerPoint.Application" {
		oleutil.MustCallMethod(dom.SingleFile, handler.CloseOp.OpType, handler.CloseOp.Arguments...)
	} else {
		oleutil.MustCallMethod(dom.Workspace, handler.CloseOp.OpType, handler.CloseOp.Arguments...)
	}
}

func (handler ConvertHandler) Quit(dom DomConvertObject) {
	oleutil.MustCallMethod(dom.Application, handler.QuitOp.OpType, handler.QuitOp.Arguments...)

不同格式的適配

支持Word/Excel/PPT轉(zhuǎn)pdf,下面是Word轉(zhuǎn)pdf的代碼:

package office

func ConvertDoc2Pdf(fileInputPath string, fileOutputPath string) {

	openArgs := []interface{}{fileInputPath}

	/// https://docs.microsoft.com/zh-cn/office/vba/api/word.document.exportasfixedformat
	exportArgs := []interface{}{fileOutputPath, 17}

	closeArgs := []interface{}{}

	quitArgs := []interface{}{}

	convertHandler := ConvertHandler{
		FileInPath:      fileInputPath,
		FileOutPath:     fileOutputPath,
		ApplicationName: "Word.Application",
		WorkspaceName:   "Documents",
		Visible:         false,
		DisplayAlerts:   0,
		OpenFileOp: Operation{
			OpType:    "Open",
			Arguments: openArgs,
		},
		ExportOp: Operation{
			OpType:    "ExportAsFixedFormat",
			Arguments: exportArgs,
		},
		CloseOp: Operation{

			OpType:    "Close",
			Arguments: closeArgs,
		},
		QuitOp: Operation{

			OpType:    "Quit",
			Arguments: quitArgs,
		},
	}
	convertHandler.Convert()
}

提供web service接口

package web

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"office-convert/office"
	"os"
	"path"
	"path/filepath"
	"runtime/debug"
	"strconv"

	log "github.com/sirupsen/logrus"
)

const PORT = 10000
const SAVED_DIR = "files"

type ConvertRequestInfo struct {
	FileInUrl  string `json:"file_in_url"`
	SourceType string `json:"source_type"`
	TargetType string `json:"target_type"`
}

func logStackTrace(err ...interface{}) {
	log.Println(err)
	stack := string(debug.Stack())
	log.Println(stack)
}

func convertHandler(w http.ResponseWriter, r *http.Request) {
	defer func() {
		if r := recover(); r != nil {
			w.WriteHeader(503)
			fmt.Fprintln(w, r)
			logStackTrace(r)
		}
	}()
	if r.Method != "POST" {
		w.WriteHeader(400)
		fmt.Fprintf(w, "Method not support")
		return
	}

	var convertRequestInfo ConvertRequestInfo
	reqBody, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Println(err)
	}
	json.Unmarshal(reqBody, &convertRequestInfo)

	log.Println(convertRequestInfo)
	log.Println(convertRequestInfo.FileInUrl)

	downloadFile(convertRequestInfo.FileInUrl)

	fileOutAbsPath := getFileOutAbsPath(convertRequestInfo.FileInUrl, convertRequestInfo.TargetType)
	convert(convertRequestInfo)

	w.WriteHeader(http.StatusOK)
	w.Header().Set("Content-Type", "application/octet-stream")
	//文件過大的話考慮使用io.Copy進行流式拷貝
	outFileBytes, err := ioutil.ReadFile(fileOutAbsPath)
	if err != nil {
		panic(err)
	}
	w.Write(outFileBytes)

}

func convert(convertRequestInfo ConvertRequestInfo) {

	fileOutAbsPath := getFileOutAbsPath(convertRequestInfo.FileInUrl, convertRequestInfo.TargetType)
	switch convertRequestInfo.SourceType {
	case "doc", "docx":
		office.ConvertDoc2Pdf(getFileInAbsPath(convertRequestInfo.FileInUrl), fileOutAbsPath)
		break
	case "xls", "xlsx":
		office.ConvertXsl2Pdf(getFileInAbsPath(convertRequestInfo.FileInUrl), fileOutAbsPath)
		break
	case "ppt", "pptx":
		office.ConvertPPT2Pdf(getFileInAbsPath(convertRequestInfo.FileInUrl), fileOutAbsPath)
		break
	}
}

func getNameFromUrl(inputUrl string) string {
	u, err := url.Parse(inputUrl)
	if err != nil {
		panic(err)
	}
	return path.Base(u.Path)
}

func getCurrentWorkDirectory() string {
	cwd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	return cwd
}

func getFileInAbsPath(url string) string {
	fileName := getNameFromUrl(url)
	currentWorkDirectory := getCurrentWorkDirectory()
	absPath := filepath.Join(currentWorkDirectory, SAVED_DIR, fileName)
	return absPath
}

func getFileOutAbsPath(fileInUrl string, targetType string) string {
	return getFileInAbsPath(fileInUrl) + "." + targetType
}

func downloadFile(url string) {
	log.Println("Start download file url :", url)
	resp, err := http.Get(url)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	fileInAbsPath := getFileInAbsPath(url)
	dir := filepath.Dir(fileInAbsPath)
	// log.Println("dir is " + dir)
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		log.Println("dir is not exists")
		os.MkdirAll(dir, 0644)
	}
	out, err := os.Create(fileInAbsPath)
	log.Println("save file to " + fileInAbsPath)
	if err != nil {
		panic(err)
	}

	defer out.Close()

	_, err = io.Copy(out, resp.Body)
	if err != nil {
		panic(err)
	}

	log.Println("Download file end url :", url)
}

func StartServer() {

	log.Println("start service ...")
	http.HandleFunc("/convert", convertHandler)
	http.ListenAndServe("127.0.0.1:"+strconv.Itoa(PORT), nil)
}

部署/使用

編譯 (可跳過)

如果要編譯源碼,得到exe文件,可以執(zhí)行命令go build -ldflags "-H windowsgui" 生成 office-convert.exe 。不想編譯的話,可以在prebuilt下找到對應(yīng)exe文件。

運行

方法一:普通運行

雙擊執(zhí)行 office-convert.exe 即可,但是如果程序報錯,或者電腦異常關(guān)機,不會重啟

方法二:后臺運行(定時任務(wù)啟動,可以自動恢復)

windows要做到定時啟動/自動恢復,還挺麻煩的。。。

1、復制文件

將prebuilt下兩個文件復制到 C:\Users\Administrator\OfficeConvert\ 目錄下

2、修改COM訪問權(quán)限

當我們以服務(wù)、定時任務(wù)啟動程序的時候,會報錯,提示空指針錯誤。

原因就是微軟限制了COM組件在非UI Session的情況下使用(防止惡意病毒之類),如果要允許,需要做如下處理:

參考這里

  • Open Component Services (Start -> Run, type in dcomcnfg)
  • Drill down to Component Services -> Computers -> My Computer and click on DCOM Config
  • Right-click on Microsoft Excel Application and choose Properties
  • In the Identity tab select This User and enter the ID and password of an interactive user account (domain or local) and click Ok

注意,上圖是演示,賬號密碼填寫該機器的Administrator賬號密碼

3、定時任務(wù)

創(chuàng)建windows定時任務(wù),每1分鐘調(diào)用check_start.bat文件,該文件自動檢查office-convert.exe是否運行,沒有就啟動。

注意: 上圖只是演示,具體位置填寫 C:\Users\Administrator\OfficeConvert\check_start.bat

Web部署

使用nginx作為反向代理,具體位置在 C:\Users\Administrator\nginx-1.20.2\nginx-1.20.2下,修改conf/nginx.conf文件,代理127.0.0.1:10000即可,
有公網(wǎng)IP(比如xxx.com)的話,配置DNS解析convert-tools.xxx.com到此機器ip。

server {
        listen       80;
        server_name  convert-tools.xxx.net;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
            proxy_pass http://127.0.0.1:10000;
        }
        # ...其他設(shè)置
}

請求

已部署到Windows機器,訪問URL:http://127.0.0.1:10000 (如果上面配置了域名,則訪問 http://convert-tools.xxx.com/convert)

請求相關(guān)

Method : POST

Content-Type: application/json

Body:

{
    "file_in_url":"https://your_docx_file_url",
    "source_type":"docx",
    "target_type":"pdf"
}
參數(shù) 是否必須取值范圍說明
file_in_url滿足下面source_type的各類文檔url待轉(zhuǎn)換的文檔的網(wǎng)絡(luò)連接
source_type[doc,docx,xls,xlsx,ppt,pptx]文檔類型
target_typepdf暫時只支持PDF,后續(xù)會支持更多

響應(yīng)

根據(jù)HTTP狀態(tài)碼做判斷

200 : ok

其他: 有錯

Body:

轉(zhuǎn)換的文件的二進制流

如果status_code非200,是對應(yīng)的報錯信息

到此這篇關(guān)于基于golang編寫一個word/excel/ppt轉(zhuǎn)pdf的工具的文章就介紹到這了,更多相關(guān)go word/excel/ppt轉(zhuǎn)pdf內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang實現(xiàn)檢測SSL證書有效期

    Golang實現(xiàn)檢測SSL證書有效期

    本文主要為大家詳細介紹如何使用Go語言中的crypto/tls模塊來獲取網(wǎng)站的SSL/TLS證書信息并檢查其有效期,文中的示例代碼講解詳細,感興趣的小伙伴可以了解下
    2026-04-04
  • 使用Go開發(fā)一個文件同步小工具(附源碼)

    使用Go開發(fā)一個文件同步小工具(附源碼)

    這篇文章主要為大家詳細介紹了如何使用Go開發(fā)一個文件同步小工具并附上源碼,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2025-11-11
  • 超詳細Go語言中JSON處理技巧分享

    超詳細Go語言中JSON處理技巧分享

    這篇文章主要為大家總結(jié)了go語言中對JSON數(shù)據(jù)結(jié)構(gòu)和結(jié)構(gòu)體之間相互轉(zhuǎn)換問題及解決方法,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2023-06-06
  • Go語言用Gin實現(xiàn)圖書管理接口的實現(xiàn)示例

    Go語言用Gin實現(xiàn)圖書管理接口的實現(xiàn)示例

    本文主要介紹了Go語言用Gin實現(xiàn)圖書管理接口的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-08-08
  • 使用go mod導入本地自定義包問題

    使用go mod導入本地自定義包問題

    這篇文章主要介紹了使用go mod導入本地自定義包問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Go語言中嵌入C語言的方法

    Go語言中嵌入C語言的方法

    這篇文章主要介紹了Go語言中嵌入C語言的方法,實例分析了Go語言中cgo工具的使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-02-02
  • 一文搞懂Go語言中條件語句的使用

    一文搞懂Go語言中條件語句的使用

    這篇文章主要介紹了Go語言中五個常用條件語句的使用,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-04-04
  • Go語言錯誤和異常實現(xiàn)

    Go語言錯誤和異常實現(xiàn)

    本文介紹了Go語言的錯誤處理機制,包括通過返回error類型顯式處理錯誤,以及使用panic和recover應(yīng)對嚴重異常,感興趣的可以了解一下
    2025-10-10
  • Golang中的godoc使用簡介(推薦)

    Golang中的godoc使用簡介(推薦)

    Godoc是go語言的文檔化工具,類似于文檔化工具godoc,類似于Python的Docstring和Java的Javadoc,這篇文章主要介紹了Golang中的godoc使用簡介,需要的朋友可以參考下
    2022-10-10
  • 一文帶你了解Go語言中time包的時間常用操作

    一文帶你了解Go語言中time包的時間常用操作

    在日常開發(fā)中,我們避免不了時間的使用,我們可能需要獲取當前時間,然后格式化保存,也可能需要在時間類型與字符串類型之間相互轉(zhuǎn)換等。本文將會對?Go?time?包里面的常用函數(shù)和方法進行介紹,需要的可以參考一下
    2022-12-12

最新評論

本溪| 沽源县| 连州市| 东明县| 双柏县| 共和县| 太湖县| 久治县| 苏尼特右旗| 洛川县| 横峰县| 灵山县| 保康县| 武威市| 克东县| 凌云县| 临清市| 克山县| 会理县| 彭州市| 水富县| 临夏市| 钟祥市| 阿拉尔市| 香格里拉县| 库车县| 洛阳市| 南岸区| 涟水县| 合水县| 平塘县| 金坛市| 客服| 全椒县| 磐安县| 宜良县| 长白| 桑植县| 宁晋县| 旬阳县| 蓬安县|