java實現word轉pdf線上預覽(前端使用PDF.js;後端使用openoffice、aspose)

来源:https://www.cnblogs.com/ph7seven/archive/2018/12/21/10158489.html
-Advertisement-
Play Games

java word轉pdf openoffice aspose ...


背景

  之前一直是用戶點擊下載word文件到本地,然後使用office或者wps打開。需求優化,要實現可以直接線上預覽,無需下載到本地然後再打開。

  隨後開始上網找資料,網上資料一大堆,方案也各有不同,大概有這麼幾種方案:

  1.word轉html然後轉pdf

  2.Openoffice + swftools + Flexmapper + jodconverter

  3.kkFileView

  分析之後最後決定使用Openoffice+PDF.js方式實現

環境搭建

  1.安裝Openoffice,下載地址:http://www.openoffice.org/download/index.html

  安裝完成之後,cmd進入安裝目錄執行命令:soffice "-accept=socket,host=localhost,port=8100;urp;StarOffice.ServiceManager" -nologo -headless -nofirststartwizard

  

  2.PDF.js,下載地址:http://mozilla.github.io/pdf.js/

  下載之後解壓,目錄結構如下:

  

  

  

代碼實現

  編碼方面,分前端後:

  後端:java後端使用openoffice把word文檔轉換成pdf文件,返迴流

  前端:把PDF.js解壓後的文件加到項目中,修改對應路徑,PDF.js拿到後端返回的流直接展示

後端

  項目使用springboot,pom文件添加依賴

<!-- openoffice word轉pdf -->
        <dependency>
            <groupId>com.artofsolving</groupId>
            <artifactId>jodconverter</artifactId>
            <version>2.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.openoffice</groupId>
            <artifactId>jurt</artifactId>
            <version>3.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.openoffice</groupId>
            <artifactId>ridl</artifactId>
            <version>3.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.openoffice</groupId>
            <artifactId>juh</artifactId>
            <version>3.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.openoffice</groupId>
            <artifactId>unoil</artifactId>
            <version>3.0.1</version>
        </dependency>

  application.properties配置openoffice服務地址與埠

openoffice.host=127.0.0.1
openoffice.port=8100

  doc文件轉pdf文件

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ConnectException;

import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.xxx.utils.Doc2PdfUtil;

@Controller
@RequestMapping("/doc2PdfController")
public class Doc2PdfController {
    @Value("${openoffice.host}")
    private String OpenOfficeHost;
    @Value("${openoffice.port}")
    private Integer OpenOfficePort;
    
    private Logger logger = LoggerFactory.getLogger(Doc2PdfController.class);
    
    @RequestMapping("/doc2pdf")
    public void doc2pdf(String fileName,HttpServletResponse response){
        File pdfFile = null;
        OutputStream outputStream = null;
        BufferedInputStream bufferedInputStream = null;
        
        Doc2PdfUtil doc2PdfUtil = new Doc2PdfUtil(OpenOfficeHost, OpenOfficePort);
        
        try {
            //doc轉pdf,返回pdf文件
            pdfFile = doc2PdfUtil.doc2Pdf(fileName);
            outputStream = response.getOutputStream();
            response.setContentType("application/pdf;charset=UTF-8");  
            bufferedInputStream = new BufferedInputStream(new FileInputStream(pdfFile));  
            byte buffBytes[] = new byte[1024];  
            outputStream = response.getOutputStream();  
            int read = 0;    
            while ((read = bufferedInputStream.read(buffBytes)) != -1) {    
                outputStream.write(buffBytes, 0, read);    
            }
        } catch (ConnectException e) {
            logger.info("****調用Doc2PdfUtil doc轉pdf失敗****");
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }  finally {
            if(outputStream != null){
                try {
                    outputStream.flush();
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }    
            }
            if(bufferedInputStream != null){
                try {
                    bufferedInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
import java.io.File;
import java.net.ConnectException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.artofsolving.jodconverter.DocumentConverter;
import com.artofsolving.jodconverter.openoffice.connection.OpenOfficeConnection;
import com.artofsolving.jodconverter.openoffice.connection.SocketOpenOfficeConnection;
import com.artofsolving.jodconverter.openoffice.converter.StreamOpenOfficeDocumentConverter;

public class Doc2PdfUtil {
    private String OpenOfficeHost; //openOffice服務地址
    private Integer OpenOfficePort; //openOffice服務埠
    
    public Doc2PdfUtil(){
    }

    public Doc2PdfUtil(String OpenOfficeHost, Integer OpenOfficePort){
        this.OpenOfficeHost = OpenOfficeHost;
        this.OpenOfficePort = OpenOfficePort;
    }
    
    private Logger logger = LoggerFactory.getLogger(Doc2PdfUtil.class);
    
    /**
     * doc轉pdf
     * @return pdf文件路徑
     * @throws ConnectException
     */
    public File doc2Pdf(String fileName) throws ConnectException{
        File docFile = new File(fileName + ".doc");
        File pdfFile = new File(fileName + ".pdf");
        if (docFile.exists()) {
            if (!pdfFile.exists()) {
                OpenOfficeConnection connection = new SocketOpenOfficeConnection(OpenOfficeHost, OpenOfficePort);
                try {
                    connection.connect();
                    DocumentConverter converter = new StreamOpenOfficeDocumentConverter(connection);
                    //最核心的操作,doc轉pdf
                    converter.convert(docFile, pdfFile);
                    connection.disconnect();
                    logger.info("****pdf轉換成功,PDF輸出:" + pdfFile.getPath() + "****");
                } catch (java.net.ConnectException e) {
                    logger.info("****pdf轉換異常,openoffice服務未啟動!****");
                    e.printStackTrace();
                    throw e;
                } catch (com.artofsolving.jodconverter.openoffice.connection.OpenOfficeException e) {
                    System.out.println("****pdf轉換器異常,讀取轉換文件失敗****");
                    e.printStackTrace();
                    throw e;
                } catch (Exception e) {
                    e.printStackTrace();
                    throw e;
                }
            }
        } else {
            logger.info("****pdf轉換異常,需要轉換的doc文檔不存在,無法轉換****");
        }
        return pdfFile;
    }
}

前端

  

  把pdfjs-2.0.943-dist下的兩個文件夾build、web整體加到項目中,然後把viewer.html改成viewer.jsp,並調整了位置,去掉了預設的pdf文件compressed.tracemonkey-pldi-09.pdf,將來使用我們生成的文件

  viewer.jsp、viewer.js註意點:

  1.引用的js、css路徑要修改過來

  2.viewer.jsp中調用pdf/web/viewer.js,viewer.js中配置了預設的pdf文件路徑,我們要動態生成pdf,因此需要修改,在jsp中定義一個參數DEFAULT_URL,然後在js中使用它

  3.jsp中寫了一個ajax獲取pdf流,之後賦值給DEFAULT_URL,然後再讓viewer.js去載入,因此需要把/pdf/web/viewer.js放到ajax方法後面

  4.viewer.js中把compressed.tracemonkey-pldi-09.pdf改成我們定義的變數DEFAULT_URL;pdf.worker.js的路徑修改成對應路徑

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<!--
Copyright 2012 Mozilla Foundation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Adobe CMap resources are covered by their own copyright but the same license:

    Copyright 1990-2015 Adobe Systems Incorporated.

See https://github.com/adobe-type-tools/cmap-resources
-->
<html dir="ltr" mozdisallowselectionprint>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <meta name="google" content="notranslate">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <c:set var="qtpath" value="${pageContext.request.contextPath}"/>
    <script>
        var qtpath = '${qtpath}';
        var fileName = '${fileName}';
    </script>
    
    <title>PDF.js viewer</title>


    <link rel="stylesheet" href="${qtpath}/res/pdf/web/viewer.css">


<!-- This snippet is used in production (included from viewer.html) -->
<link rel="resource" type="application/l10n" href="${qtpath}/res/pdf/web/locale/locale.properties">
<script type="text/javascript" src="${qtpath}/res/js/jquery/jquery-2.1.4.min.js"></script>
<script type="text/javascript">
    var DEFAULT_URL = "";//註意,刪除的變數在這裡重新定義  
    var PDFData = "";  
    $.ajax({  
        type:"post",  
        async:false,  //
        mimeType: 'text/plain; charset=x-user-defined',  
        url:'${qtpath}/doc2PdfController/doc2pdf',
        data:{'fileName':fileName},
        success:function(data){  
           PDFData = data;  
        }  
    });  
    var rawLength = PDFData.length;  
    //轉換成pdf.js能直接解析的Uint8Array類型,見pdf.js-4068  
    var array = new Uint8Array(new ArrayBuffer(rawLength));    
    for(i = 0; i < rawLength; i++) {  
      array[i] = PDFData.charCodeAt(i) & 0xff;  
    }  
    DEFAULT_URL = array;
</script>
<script type="text/javascript" src="${qtpath}/res/pdf/build/pdf.js"></script>
<script type="text/javascript" src="${qtpath}/res/pdf/web/viewer.js"></script>

  </head>

  ...

 效果

 

分割線

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  本以為完美的實現了doc線上預覽,上測試環境後發現了一個大坑,我們的doc文件不是在本地office創建後上傳的,是其他同事用freemarker ftl模板生成的,這種生成的doc文件根本不是微軟標準的doc,本質是xml數據結構,openoffice拿這種文件去轉換pdf文件直接就報錯了

 

 

 

  上網查資料查了半天也沒找到這種問題的解決方案,想想只能是放棄openoffice改用其他方法了(freemarker ftl生成doc這個肯定是不能動的)

  看到一些博客使用word--html--pdf生成pdf,還有的使用freemarker ftl xml 生成pdf感覺還是太繁瑣了,我只是想拿現有的doc(雖然是freemarker ftl生成的)轉換成pdf啊

  繼續看博客查資料,看到一種方法,使用aspose把doc轉換成pdf,抱著試一試的心態在本地測試了下,沒想到竟然成了,感覺太意外了,aspose方法超級簡單,只要導入jar包,幾行代碼就可以搞定,並且轉換速度比openoffice要快很多。很是奇怪,這麼好用這麼簡單的工具為什麼沒在我一開始搜索word轉pdf的時候就出現呢

aspose doc轉pdf

  在maven倉庫搜索aspose,然後把依賴加入pom.xml發現jar包下載不下來,沒辦法,最後在csdn下載aspose jar包,然後mvn deploy到倉庫

  pom.xml

<!-- word轉pdf maven倉庫沒有需要本地jar包發佈到私服 -->
        <dependency>
            <groupId>com.aspose.words</groupId>
            <artifactId>aspose-words-jdk16</artifactId>
            <version>14.9.0</version>
        </dependency>

  

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ConnectException;

import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.xxx.utils.Doc2PdfUtil;

@Controller
@RequestMapping("/doc2PdfController")
public class Doc2PdfController {
    
    private Logger logger = LoggerFactory.getLogger(Doc2PdfController.class);
    
    @RequestMapping("/doc2pdf")
    public void doc2pdf(String fileName,HttpServletResponse response){
        File pdfFile = null;
        OutputStream outputStream = null;
        BufferedInputStream bufferedInputStream = null;
        String docPath = fileName + ".doc";
        String pdfPath = fileName + ".pdf";
        try {
            pdfFile = Doc2PdfUtil.doc2Pdf(docPath, pdfPath);
            outputStream = response.getOutputStream();
            response.setContentType("application/pdf;charset=UTF-8");  
            bufferedInputStream = new BufferedInputStream(new FileInputStream(pdfFile));  
            byte buffBytes[] = new byte[1024];  
            outputStream = response.getOutputStream();  
            int read = 0;    
            while ((read = bufferedInputStream.read(buffBytes)) != -1) {    
                outputStream.write(buffBytes, 0, read);    
            }
        } catch (ConnectException e) {
            logger.info("****調用Doc2PdfUtil doc轉pdf失敗****");
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }  finally {
            if(outputStream != null){
                try {
                    outputStream.flush();
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }    
            }
            if(bufferedInputStream != null){
                try {
                    bufferedInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 

  Doc2PdfUtil.java

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.aspose.words.License;
import com.aspose.words.SaveFormat;

public class Doc2PdfUtil {
    
    private static Logger logger = LoggerFactory.getLogger(Doc2PdfUtil.class);
    
    /**
     * doc轉pdf
     * @param docPath doc文件路徑,包含.doc
     * @param pdfPath pdf文件路徑,包含.pdf
     * @return
     */
    public static File doc2Pdf(String docPath, String pdfPath){
        File pdfFile = new File(pdfPath);
        try {
            String s = "<License><Data><Products><Product>Aspose.Total for Java</Product><Product>Aspose.Words for Java</Product></Products><EditionType>Enterprise</EditionType><SubscriptionExpiry>20991231</SubscriptionExpiry><LicenseExpiry>20991231</LicenseExpiry><SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber></Data><Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=</Signature></License>";
            ByteArrayInputStream is = new ByteArrayInputStream(s.getBytes());
            License license = new License();
            license.setLicense(is);
            com.aspose.words.Document document = new com.aspose.words.Document(docPath);
            document.save(new FileOutputStream(pdfFile),SaveFormat.PDF);
        } catch (Exception e) {
            logger.info("****aspose doc轉pdf異常");
            e.printStackTrace();
        }
        return pdfFile;
    }
}

  aspose-words-jdk16-14.9.0.jar下載地址

  https://download.csdn.net/download/u013279345/10868189

 

window下正常,linux下亂碼的解決方案

  使用com.aspose.words將word模板轉為PDF文件時,在開發平臺window下轉換沒有問題,中文也不會出現亂碼。但是將服務部署在正式伺服器(Linux)上,轉換出來的PDF中文就出現了亂碼。在網上找了很久,才找到原因,現將解決辦法分享給大家。

一、問題原因分析

     在window下沒有問題但是在linux下有問題,就說明不是代碼或者輸入輸出流編碼的問題,根本原因是兩個平臺環境的問題。出現亂碼說明linux環境中沒有相應的字體以供使用,所以就會導致亂碼的出現。將轉換無問題的windos主機中的字體拷貝到linux平臺下進行安裝,重啟伺服器後轉換就不會出現亂碼了。

二、window字體複製到linux環境並安裝

按照教程安裝完成後重啟linux伺服器即可搞定亂碼問題。

1. From Windows

Windows下字體庫的位置為C:\Windows\fonts,這裡麵包含所有windows下可用的字體。

2. To Linux  

linux的字體庫是 /usr/share/Fonts 。

在該目錄下新建一個目錄,比如目錄名叫 windows(根據個人的喜好,自己理解就行,當然這裡是有許可權要求的,你可以用sudo來執行)。

然後將 windows 字體庫中你要的字體文件複製到新建的目錄下(只需要複製*.ttc,和*.ttf的文件).


複製所有字體:

   sudo cp *.ttc /usr/share/fonts/windows/   
   sudo cp *.ttf /usr/share/fonts/windows/    

更改這些字體庫的許可權:
    sudo chmod 755 /usr/share/fonts/windows/*   

然後進入Linux字體庫:
    cd /usr/share/fonts/windows/   

接著根據當前目錄下的字體建立scale文件
    sudo mkfontscale    

接著建立dir文件
   sudo mkfontdir    

然後運行
   sudo fc-cache    

重啟 Linux 操作系統就可以使用這些字體了。

 

linux下亂碼問題解決方案轉載自:

https://blog.csdn.net/hanchuang213/article/details/64905214

https://blog.csdn.net/shanelooli/article/details/7212812


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 超融合基礎架構的簡單定義: 超融合基礎架構(Hyper-Converged Infrastructure 簡稱HCL)也被稱為超融合架構,是指在同一套單元設備中不僅僅具備計算,網路,存儲和伺服器虛擬化等資源和技術,而且還包括了“緩存加速,重覆數據刪除,線上數據壓縮,備份軟體,快照技術”等等各種元素。 ...
  • 閱讀指南: 精讀一章內容,手工輸入一章代碼(註1),與書中描述的思想進行印證,實在搞不懂就放過吧。設計模式絕對不會一次就看懂的。 這本書對於理解設計模式很有幫助,就是例子不太符合中國人的思維模式,但是堅持下去肯定會搞明白的。 全書精華: 1. Chap12的Ducks,通過一點點重構Ducks程式,... ...
  • 之前做數據分頁遇到這樣一個需求,就是數據到最後一頁的時候不能中斷,繼續把第一頁的數據追加到後面,無限顯示下去。 ...
  • 註冊功能之圖片驗證碼: 1.實現過程: 傳遞uuid給後端,再發送圖片驗證碼的請求給後端,後端存儲uuid並生成圖片驗證碼保存到redis,然後將圖片驗證碼返回給前端。 當用戶輸入圖片驗證碼的時候,前端會發送uuid和用戶輸入的圖片驗證碼內容給後端,後端進行比較校驗。 2.實現步驟: 後端:實現介面 ...
  • 想必大家在熟悉不過了,不錯今天就遇到了這個萬年坑,哪怕喜歡翻源碼的人,也不屑一顧翻它的源碼,良言相勸最好翻下源碼。 1. String為啥被定義為final ? 2. String是線程安全的麽 ? 3. String的設計思想是什麼 ? 4. 偏移量是什麼 ? 在不看下麵解釋的時候自己想想,是不是 ...
  • 元組類型簡介 使用括弧包圍的數據結構是元組(tuple)。例如: 元組和列表一樣,都是容器型的數據結構,且都是序列,所以容器中的元素都是按照索引位置有序存放的。所以,可以進行索引取值、切片等序列通用操作。 不同的是,元組是不可變序列,無法原處修改,意味著修改元組必須創建新的元組對象。實際上元組的概念 ...
  • builder模式,目的在於:抽離複雜對象的構造函數,讓我們可以通過多種方法的排列組合構建複雜的對象。如果構造器參數過多,可以考慮 builder 模式 ...
  • 插件截圖 插件簡介 此插件在發佈文章的時候自動向百度熊掌號提交,有利於百度熊掌號收錄。基於Emlog6.0.1特別版美化的插件。 在百度推送插件的基礎上修改製作而成與百度推送共存,解放雙手,走向人生巔峰! 註:請在https://ziyuan.baidu.com/xzh/home/index 獲取a ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...