openOffice word轉pdf,pdf轉圖片優化版

来源:https://www.cnblogs.com/liran123/archive/2019/05/17/10883582.html
-Advertisement-
Play Games

之前寫了一個版本的,不過代碼繁瑣而且不好用,效率有些問題。尤其pdf轉圖片速度太慢。下麵是優化版本的代碼。 spriing_boot 版本信息:2.0.1.RELEASE 1、配置信息: application.yml 2、轉換入口 pdf 轉圖片參考 https://gitee.com/cycmy ...


之前寫了一個版本的,不過代碼繁瑣而且不好用,效率有些問題。尤其pdf轉圖片速度太慢。下麵是優化版本的代碼。

spriing_boot 版本信息:2.0.1.RELEASE

1、配置信息:

package com.yunfatong.conf;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.jodconverter.DocumentConverter;
import org.jodconverter.LocalConverter;
import org.jodconverter.office.LocalOfficeManager;
import org.jodconverter.office.OfficeManager;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashSet;
import java.util.Set;

/**
 * openoffice 配置
 *
 * @author liran
 * @date 20190517
 */
@Configuration
@ConditionalOnClass({DocumentConverter.class})
@ConditionalOnProperty(prefix = "jodconverter", name = {"enabled"}, havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties({JodConverterProperties.class})
@Slf4j
public class JodConverterAutoConfiguration {
    private final JodConverterProperties properties;

    public JodConverterAutoConfiguration(JodConverterProperties properties) {
        this.properties = properties;
    }

    private OfficeManager createOfficeManager() {
        LocalOfficeManager.Builder builder = LocalOfficeManager.builder();
        if (!StringUtils.isBlank(this.properties.getPortNumbers())) {
            Set<Integer> iports = new HashSet<>();
            String[] var3 = StringUtils.split(this.properties.getPortNumbers(), ", ");
            int var4 = var3.length;

            for (int var5 = 0; var5 < var4; ++var5) {
                String portNumber = var3[var5];
                iports.add(NumberUtils.toInt(portNumber, 2002));
            }

            builder.portNumbers(ArrayUtils.toPrimitive(iports.toArray(new Integer[iports.size()])));
        }
        builder.officeHome(this.properties.getOfficeHome());
        builder.workingDir(this.properties.getWorkingDir());
        builder.templateProfileDir(this.properties.getTemplateProfileDir());
        builder.killExistingProcess(this.properties.isKillExistingProcess());
        builder.processTimeout(this.properties.getProcessTimeout());
        builder.processRetryInterval(this.properties.getProcessRetryInterval());
        builder.taskExecutionTimeout(this.properties.getTaskExecutionTimeout());
        builder.maxTasksPerProcess(this.properties.getMaxTasksPerProcess());
        builder.taskQueueTimeout(this.properties.getTaskQueueTimeout());
        return builder.build();
    }

    @Bean(initMethod = "start", destroyMethod = "stop")
    @ConditionalOnMissingBean
    public OfficeManager officeManager() {
        return this.createOfficeManager();
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean({OfficeManager.class})
    public DocumentConverter jodConverter(OfficeManager officeManager) {
        return LocalConverter.make(officeManager);
    }
}
package com.yunfatong.conf;

import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.regex.Pattern;

/**
 * openoffice 配置
 *
 * @author liran
 * @date 20190517
 */
@ConfigurationProperties("jodconverter")
public class JodConverterProperties {
    private boolean enabled;
    private String officeHome;
    private String portNumbers = "2002";
    private String workingDir;
    private String templateProfileDir;
    private boolean killExistingProcess = true;
    private long processTimeout = 120000L;
    private long processRetryInterval = 250L;
    private long taskExecutionTimeout = 120000L;
    private int maxTasksPerProcess = 200;
    private long taskQueueTimeout = 30000L;

    public JodConverterProperties() {
    }

    public boolean isEnabled() {
        return this.enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public String getOfficeHome() {
        //根據不同系統分別設置
        //office-home: C:\Program Files (x86)\OpenOffice 4  #windows下預設 不用修改
        // #office-home: /opt/openoffice4      #linux 預設 不用修改
        String osName = System.getProperty("os.name");
        if (Pattern.matches("Linux.*", osName)) {
            this.officeHome = "/opt/openoffice4";
        } else if (Pattern.matches("Windows.*", osName)) {
            this.officeHome = "C:\\Program Files (x86)\\OpenOffice 4";
        } else {
            this.officeHome = "/opt/openoffice4";
        }
        return this.officeHome;
    }

    public void setOfficeHome(String officeHome) {
        this.officeHome = officeHome;
    }

    public String getPortNumbers() {
        return this.portNumbers;
    }

    public void setPortNumbers(String portNumbers) {
        this.portNumbers = portNumbers;
    }

    public String getWorkingDir() {
        return this.workingDir;
    }

    public void setWorkingDir(String workingDir) {
        this.workingDir = workingDir;
    }

    public String getTemplateProfileDir() {
        return this.templateProfileDir;
    }

    public void setTemplateProfileDir(String templateProfileDir) {
        this.templateProfileDir = templateProfileDir;
    }

    public boolean isKillExistingProcess() {
        return this.killExistingProcess;
    }

    public void setKillExistingProcess(boolean killExistingProcess) {
        this.killExistingProcess = killExistingProcess;
    }

    public long getProcessTimeout() {
        return this.processTimeout;
    }

    public void setProcessTimeout(long processTimeout) {
        this.processTimeout = processTimeout;
    }

    public long getProcessRetryInterval() {
        return this.processRetryInterval;
    }

    public void setProcessRetryInterval(long procesRetryInterval) {
        this.processRetryInterval = procesRetryInterval;
    }

    public long getTaskExecutionTimeout() {
        return this.taskExecutionTimeout;
    }

    public void setTaskExecutionTimeout(long taskExecutionTimeout) {
        this.taskExecutionTimeout = taskExecutionTimeout;
    }

    public int getMaxTasksPerProcess() {
        return this.maxTasksPerProcess;
    }

    public void setMaxTasksPerProcess(int maxTasksPerProcess) {
        this.maxTasksPerProcess = maxTasksPerProcess;
    }

    public long getTaskQueueTimeout() {
        return this.taskQueueTimeout;
    }

    public void setTaskQueueTimeout(long taskQueueTimeout) {
        this.taskQueueTimeout = taskQueueTimeout;
    }
}

application.yml 

jodconverter:
  enabled: true
  office-home: linuxOrwindows
  #/opt/openoffice4      #linux 預設 不用修改 C:\Program Files (x86)\OpenOffice 4  #windows下預設 不用修改
  port-numbers: 2002
  max-tasks-per-process: 10

 

2、轉換入口

package com.yunfatong.ojd.util.pdf;

import cn.hutool.core.date.DatePattern;
import com.yunfatong.ojd.common.exception.CommonException;
import com.yunfatong.ojd.service.FileSystemStorageService;
import com.yunfatong.ojd.util.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.jodconverter.DocumentConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

/**
* word 轉pdf
*
* @author lr
*/
@Component
@Slf4j
public class TransferUtil {
//這裡沒有@Autowired 主要是配置不啟用的話 無法註入
private DocumentConverter documentConverter;
@Autowired
private FileSystemStorageService fileSystemStorageService;
/**
* fileSystemStorageService 就是拼接出本地路徑的作用
* storage.winLocation=D:\\ojd\\upload\\images\\
* ##上傳圖片linux存儲路徑
* storage.linuxLocation=/home/ojd/upload/images/
*/

final static String WORD_SUFFIX_DOC = "doc";
final static String WORD_SUFFIX_DOCX = "docx";

/**
* word ->pdf
*
* @param webPath 瀏覽器可訪問路徑(資料庫存的)如 /test/wd.word
* @return 相同文件夾下的轉換後的pdf 路徑 如/test/wd_20190517151515333.pdf
* @throws Exception
*/
public String transferWordToPdf(String webPath) throws Exception {
if(documentConverter==null){
documentConverter = SpringUtil.getBean(DocumentConverter.class);
}
//轉換成本地實際磁碟路徑
String originLocalFilePath = fileSystemStorageService.getLocation(webPath);
File inputFile = new File(originLocalFilePath);
if (!inputFile.exists() ||
        !inputFile.isFile() ||
         (!StringUtils.contains(inputFile.getName(), WORD_SUFFIX_DOC) &&
          !StringUtils.contains(inputFile.getName(), WORD_SUFFIX_DOCX))) {
throw new CommonException("word -> pdf轉換錯誤 當前文件不是word或 文件不存在: " + webPath);
}

DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DatePattern.PURE_DATETIME_MS_PATTERN);
String timeNow = formatter.format(LocalDateTime.now());
String newPdfWebPath = StringUtils.substringBeforeLast(webPath, ".") + "_" + timeNow + ".pdf";
try {
File outputFile = new File(fileSystemStorageService.getLocation(newPdfWebPath));
documentConverter.convert(inputFile).to(outputFile).execute();
} catch (Exception e) {
log.error("word->pdf 轉換錯誤------------> Exception:{}", e);
throw e;
}
return newPdfWebPath;
}

public List<String> transferPdfToImage(String webPath) throws Exception {
String originLocalFilePath = fileSystemStorageService.getLocation(webPath);
File inputFile = new File(originLocalFilePath);
if (!inputFile.exists() ||
        !inputFile.isFile() ||
        webPath.lastIndexOf(".pdf") < 0) {
throw new CommonException("pdf-> img 源文件不是pdf文件 或者文件不存在!" + webPath);
}
String localPdfpath = fileSystemStorageService.getLocation(webPath);
String newImgWebPathPreSuffix = StringUtils.substringBeforeLast(webPath, ".");
String localImgPath = fileSystemStorageService.getLocation(newImgWebPathPreSuffix);
PdfTransferUtil pdfTranfer = new PdfTransferUtil();
List<byte[]> ins = pdfTranfer.pdf2Image(localPdfpath, "png", 1.5f);
List<String> webPaths = new ArrayList<>(ins.size());
for (int i = 0; i < ins.size(); i++) {
byte[] data = ins.get(i);
String pathReal = localImgPath + "_ojd_" + i + ".png";
FileUtils.writeByteArrayToFile(new File(pathReal), data);
webPaths.add(pathReal);
}
return webPaths;
}

}

 pdf 轉圖片參考 https://gitee.com/cycmy/pdftranfer.git

package com.yunfatong.ojd.util.pdf;

import lombok.extern.slf4j.Slf4j;
import org.icepdf.core.pobjects.Document;
import org.icepdf.core.pobjects.Page;
import org.icepdf.core.util.GraphicsRenderingHints;

import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * @author lr
 *  
 */
@Slf4j
public class PdfTransferUtil {

    //*********************************pdf to image **********************************************************

    /**
     * 將指定pdf位元組數組轉換為指定格式圖片二進位數組
     *
     * @param pdfBytes  PDF位元組數組
     * @param imageType 轉換圖片格式  預設png
     * @param zoom      縮略圖顯示倍數,1表示不縮放,0.3則縮小到30%
     * @return List<byte [ ]>
     * @throws Exception
     */
    public List<byte[]> pdf2Image(byte[] pdfBytes, String imageType, float zoom) throws Exception {
        Document document = new Document();
        document.setByteArray(pdfBytes, 0, pdfBytes.length, null);
        return pageExtraction(document, imageType, 0f, zoom);
    }

    /**
     * 將指定pdf輸入流轉換為指定格式圖片二進位數組
     *
     * @param inputPDF  PDF二進位流
     * @param imageType 轉換圖片格式 預設png
     * @param zoom      縮略圖顯示倍數,1表示不縮放,0.3則縮小到30%
     * @return List<byte [ ]>
     * @throws Exception
     */
    public List<byte[]> pdf2Image(InputStream inputPDF, String imageType, float zoom) throws Exception {

        Document document = new Document();
        document.setInputStream(inputPDF, null);
        return pageExtraction(document, imageType, 0f, zoom);
    }

    /**
     * 將指定pdf文件轉換為指定格式圖片二進位數組
     *
     * @param pdfPath   原文件路徑,例如d:/test.pdf
     * @param imageType 轉換圖片格式 預設png
     * @param zoom      縮略圖顯示倍數,1表示不縮放,0.3則縮小到30%
     * @return List<byte [ ]>
     * @throws Exception
     */
    public List<byte[]> pdf2Image(String pdfPath, String imageType, float zoom) throws Exception {
        Document document = new Document();
        document.setFile(pdfPath);
        return pageExtraction(document, imageType, 0f, zoom);
    }
    //*********************************pdf to image **********************************************************

    private List<byte[]> pageExtraction(Document document, String imageType, float rotation, float zoom) {
        // setup two threads to handle image extraction.
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        try {
            // create a list of callables.
            int pages = document.getNumberOfPages();
            List<byte[]> result = new ArrayList<byte[]>(pages);
            List<Callable<byte[]>> callables = new ArrayList<Callable<byte[]>>(pages);
            for (int i = 0; i < pages; i++) {
                callables.add(new CapturePage(document, i, imageType, rotation, zoom));
            }
            List<Future<byte[]>> listFuture = executorService.invokeAll(callables);
            executorService.submit(new DocumentCloser(document)).get();
            for (Future<byte[]> future : listFuture) {
                result.add(future.get());
            }
            return result;
        } catch (Exception ex) {
            log.error(" pdf 轉換圖片錯誤  Error handling PDF document " + ex);
        } finally {
            executorService.shutdown();
        }
        return null;
    }

    public class CapturePage implements Callable<byte[]> {
        private Document document;
        private int pageNumber;
        private String imageType;
        private float rotation;
        private float zoom;

        private CapturePage(Document document, int pageNumber, String imageType, float rotation, float zoom) {
            this.document = document;
            this.pageNumber = pageNumber;
            this.imageType = imageType;
            this.rotation = rotation;
            this.zoom = zoom;
        }

        @Override
        public byte[] call() throws Exception {
            BufferedImage image = (BufferedImage) document.getPageImage(pageNumber, GraphicsRenderingHints.SCREEN, Page.BOUNDARY_CROPBOX, rotation, zoom);
            ByteArrayOutputStream bs = new ByteArrayOutputStream();
            ImageOutputStream imOut = ImageIO.createImageOutputStream(bs);
            ImageIO.write(image, imageType, imOut);
            image.flush();
            return bs.toByteArray();
        }
    }

    /**
     * Disposes the document.
     */
    public class DocumentCloser implements Callable<Void> {
        private Document document;

        private DocumentCloser(Document document) {
            this.document = document;
        }

        @Override
        public Void call() {
            if (document != null) {
                document.dispose();
                log.info("Document disposed");
            }
            return null;
        }
    }

}

springutils

package com.yunfatong.ojd.util;

/**
 * @Auther liran
 * @Date 2018/8/30 14:49
 * @Description
 */
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if(SpringUtil.applicationContext == null) {
            SpringUtil.applicationContext = applicationContext;
        }
        System.out.println("========ApplicationContext配置成功,在普通類可以通過調用SpringUtils.getAppContext()獲取applicationContext對象,applicationContext="+SpringUtil.applicationContext+"========");

    }

    //獲取applicationContext
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    //通過name獲取 Bean.
    public static Object getBean(String name){
        return getApplicationContext().getBean(name);
    }

    //通過class獲取Bean.
    public static <T> T getBean(Class<T> clazz){
        return getApplicationContext().getBean(clazz);
    }

    //通過name,以及Clazz返回指定的Bean
    public static <T> T getBean(String name,Class<T> clazz){
        return getApplicationContext().getBean(name, clazz);
    }

}

 

pom.xml

 

  <!--word轉換pdf begin-->
        <dependency>
            <groupId>org.jodconverter</groupId>
            <artifactId>jodconverter-core</artifactId>
            <version>4.2.2</version>
        </dependency>

        <dependency>
            <groupId>org.jodconverter</groupId>
            <artifactId>jodconverter-local</artifactId>
            <version>4.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.jodconverter</groupId>
            <artifactId>jodconverter-spring-boot-starter</artifactId>
            <version>4.2.2</version>
        </dependency>
        <!--word轉換pdf end-->

        <!--PDF轉圖片-->
        <dependency>
            <groupId>org.icepdf.os</groupId>
            <artifactId>icepdf-core</artifactId>
            <version>6.2.2</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.media</groupId>
                    <artifactId>jai_core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.icepdf.os</groupId>
            <artifactId>icepdf-viewer</artifactId>
            <version>6.2.2</version>
        </dependency>
        <!--PDF轉圖片 end-->

 

3、調用測試:

import com.yunfatong.ojd.service.FileSystemStorageService;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class WordTransferPdfUtilTest {
    @Autowired
    TransferUtil wordTransferPdfUtil;
    @Autowired
    FileSystemStorageService fileSystemStorageService;


    @Test
    public void transferLocalFile() {
   try {
/*******************word 轉pdf******************/
long time = System.currentTimeMillis();
System.out.println("start :======" + time);
wordTransferPdfUtil.transferWordToPdf("courtChongqing/test_new/555.docx");
log.error(System.currentTimeMillis() + " time============================== :" + ((System.currentTimeMillis() - time) / 1000));

/*******************pdf轉圖片******************/
long time2 = System.currentTimeMillis();
List<String> pdfImages2 = wordTransferPdfUtil.transferPdfToImage("courtChongqing/test_new/333.pdf");
for (String pdfImage : pdfImages2) {
log.error(pdfImage);
}
log.error(" time===============================22222222 :" + ((System.currentTimeMillis() - time2) / 1000));
// System.out.println("pdf path =============" + path);
} catch (Exception e) {
e.printStackTrace();
}
 } }

 


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

-Advertisement-
Play Games
更多相關文章
  • 硬體記憶體架構? Java記憶體模型? 記憶體間交互的操作有哪些? 原子性、可見性、有序性? 先行發生原則有哪些? ...
  • hibernate介紹 hibernate是一個開源的輕量級的框架, hibernate框架應用在javaee三層結構中的dao層框架,在dao層對資料庫進行crud操作,使用hibernate框架實現crud操作; hibernate底層就是jdbc,hibernate對jdbc進行了封裝;使用h ...
  • Recently, I was made a service which can provide a simple way to get best model. so, i spent lot of time to read source code of auto-sklearn, auto-skl ...
  • 1、JAVA 語言如何進行異常處理,關鍵字:throws,throw,try,catch,finally分別代表什麼意義?在try 塊中可以拋出異常嗎? 答:Java 通過面向對象的方法進行異常處理,把各種不同的異常進行分類,並提供了良好的介面。在Java 中,每個異常都是一個對象,它是Throwa ...
  • 第1題:動態載入又對及時性要求很高怎麼處理? 如何知道一個網站是動態載入的數據? 用火狐或者谷歌瀏覽器 打開你網頁,右鍵查看頁面源代碼,ctrl +F 查詢輸入內容,源代碼裡面並沒有這個值,說明是動態載入數據。 1. Selenium+Phantomjs 2. 儘量不使用 sleep 而使用 Web ...
  • 斐波那契數列、(引用float(‘inf’)無窮大的特性來比對,從而提取數組中的最小值)float(“inf”)正無窮大 float(“-inf”)負無窮大 ...
  • 本隨筆旨在強化理解傳值與傳引用 如下代碼的運行結果 其中i沒有改變,s也沒有改變。 但model中的值均改變了。 i :100s :hellomodel :testchangemodel2 :changeModel i :100s :hellomodel :testchangemodel2 :cha ...
  • 有時候maven真的很坑! 有時候提示invalid LOC header (bad signat signature), 但又有時候什麼都不提示,工程報錯,情況有肯多中,不知道大家遇到過幾種詭異的. ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...