欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java壓縮和解壓縮ZIP文件實(shí)戰(zhàn)案例

 更新時(shí)間:2023年07月05日 11:19:36   作者:喵喵@香菜  
這篇文章主要給大家介紹了關(guān)于Java壓縮和解壓縮ZIP文件的相關(guān)資料,ZIP是一種較為常見的壓縮形式,最近項(xiàng)目中遇到了再Java中壓縮和解壓縮zip文件的需求,特此分享給大家,需要的朋友可以參考下

前言

在現(xiàn)代計(jì)算機(jī)上,數(shù)據(jù)傳輸和存儲(chǔ)越來越依賴于文件壓縮技術(shù)。當(dāng)我們需要發(fā)送大量數(shù)據(jù)時(shí),壓縮文件可以大大減少傳輸時(shí)間和網(wǎng)絡(luò)帶寬,而且壓縮文件還可以幫助我們節(jié)省磁盤空間。在Java中提供了壓縮和解壓縮文件的功能,可以使用java.util.zip包中的類來實(shí)現(xiàn)。本篇將對(duì)如何使用 Java 實(shí)現(xiàn)單文多件壓縮和解壓縮進(jìn)行總結(jié)。

文件壓縮指的是將一個(gè)或多個(gè)文件通過壓縮算法,將其存儲(chǔ)為一個(gè)更小的文件,以便于存儲(chǔ)和傳輸。壓縮的原理是通過對(duì)文件的數(shù)據(jù)進(jìn)行編碼和壓縮,使其占用更少的空間。壓縮后的文件可以通過解壓縮算法還原成原始的文件格式。在文件壓縮過程中,常見的操作是將多個(gè)文件打包成一個(gè)壓縮文件,例如zip、tar等格式。

Java解壓縮文件

常見的文件壓縮格式包括:

  • ZIP:最常見的壓縮文件格式之一,可以存儲(chǔ)一個(gè)或多個(gè)文件,并可在不同的操作系統(tǒng)中進(jìn)行解壓縮。
  • TAR:Linux系統(tǒng)中的常見文件壓縮格式,通常用于打包多個(gè)文件,但不會(huì)進(jìn)行壓縮。
  • GZIP:常用的文件壓縮格式,通常用于壓縮單個(gè)文件,可以獲得更高的壓縮比。
  • BZIP2:高效的壓縮算法,通常用于壓縮文本文件和XML文件等。
  • JAR: Jar包對(duì)于Java開發(fā)同學(xué)來說肯定很熟悉,其也是一個(gè)壓縮包

Java提供了多種用于壓縮和解壓縮文件的API,主要包括以下類和方法:

  • ZipOutputStream 和 ZipInputStream:用于創(chuàng)建和讀取ZIP格式的壓縮文件。
  • GZIPOutputStream 和 GZIPInputStream:用于創(chuàng)建和讀取GZIP格式的壓縮文件。
  • JarOutputStream 和 JarInputStream:用于創(chuàng)建和讀取JAR格式的壓縮文件。
  • DeflaterOutputStream 和 InflaterInputStream:用于創(chuàng)建和讀取DEFLATE格式的壓縮文件。
  • CheckedOutputStream 和 CheckedInputStream:用于在壓縮和解壓縮過程中計(jì)算文件的校驗(yàn)和。

壓縮和解壓縮ZIP文件

通過使用Java 自帶的 java.util.zip 類庫(kù)下的ZipOutputStream、ZipInputStreamZipEntry實(shí)現(xiàn)文件的壓縮和解壓縮,其中ZipOutputStream用于創(chuàng)建ZIP壓縮文件輸出流輸出ZIP壓縮文件,ZipInputStream用于創(chuàng)建ZIP文件輸入流讀取ZIP文件用于解壓縮而ZipEntry對(duì)應(yīng)ZIP壓縮包中的每個(gè)被壓縮對(duì)象;

生成ZIP文件

壓縮單個(gè)文件或者單個(gè)文件夾方法,代碼如下:

/**
 * 壓縮文件(支持單個(gè)文件和單個(gè)文件夾)
 * @param sourceFile 被壓縮文件/文件夾
 * @param zipFile Zip文件
 */
public static void zipCompress(File sourceFile, File zipFile) {
    try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile))) {
        // 設(shè)置壓縮方法
        zos.setMethod(ZipOutputStream.DEFLATED);
        zos.setLevel(Deflater.BEST_COMPRESSION); // 默認(rèn)為-1,壓縮級(jí)別,1速度快,效率低,9 速度滿,效率高
        // zos.setLevel(Deflater.BEST_SPEED);
        zos.setComment("zip文件說明");
        // 處理文件夾
        if (sourceFile.exists() && sourceFile.isDirectory() && Objects.nonNull(sourceFile.listFiles())){
            Arrays.stream(Objects.requireNonNull(sourceFile.listFiles())).forEach(file -> {
                addZipFile(file, zos);
            });
        }else{
            addZipFile(sourceFile, zos);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

為了支持讀取單個(gè)文件夾進(jìn)行壓縮,增加一個(gè)向ZipOutputStream中添加ZipEntry的方法,代碼如下:

/**
 * 向ZIP中添加文件
 * @param file 源文件
 * @param zos zip輸出流
 */
private static void addZipFile(File file, ZipOutputStream zos){
    if (!file.exists() || file.isDirectory()){
        throw new RuntimeException("文件不存在或該文件為文件夾,請(qǐng)檢查");
    }
    try {
        // 讀入文件
        FileInputStream fis = new FileInputStream(file);
        // 創(chuàng)建壓縮對(duì)象并設(shè)置一些屬性
        ZipEntry entry = new ZipEntry(file.getName());
        entry.setMethod(ZipEntry.DEFLATED); // 壓縮方法默認(rèn)為DEFLATED
        // entry.setMethod(ZipEntry.STORED); // STORED(不壓縮)。當(dāng)使用STORED壓縮方法時(shí),需要設(shè)置未壓縮的數(shù)據(jù)大小和CRC-32校驗(yàn)和,否則壓縮和解壓縮時(shí)會(huì)出現(xiàn)錯(cuò)誤。
        entry.setSize(file.length()); // 設(shè)置未壓縮的數(shù)據(jù)大小,這里設(shè)置的是文件大小
        // 計(jì)算 CRC-32 校驗(yàn)碼
        // byte[] data = Files.readAllBytes(file.toPath());
        // CRC32 crc = new CRC32();
        // crc.update(data);
        // entry.setCrc(crc.getValue()); // 設(shè)置CRC-32校驗(yàn)和,用于保證壓縮后的數(shù)據(jù)完整性,盡量別手動(dòng)設(shè)置,可以通過CRC-32計(jì)算
        entry.setCompressedSize(file.length()); // 設(shè)置壓縮后的數(shù)據(jù)大小,這里設(shè)置的是使用DEFLATED方法壓縮后的數(shù)據(jù)大小
        entry.setExtra(new byte[]{}); // 設(shè)置額外的數(shù)據(jù),這里設(shè)置為空
        entry.setComment("file comment"); // 設(shè)置ZipEntry的注釋,即文件說明
        entry.setCreationTime(FileTime.from(Instant.now())); // 設(shè)置文件的創(chuàng)建時(shí)間
        entry.setLastAccessTime(FileTime.from(Instant.now())); // 設(shè)置文件的最后訪問時(shí)間
        entry.setLastModifiedTime(FileTime.from(Instant.now())); // 設(shè)置文件的最后修改時(shí)間。
        // 向ZIP輸出流中添加一個(gè)ZIP實(shí)體,構(gòu)造方法中的name參數(shù)指定文件在ZIP包中的文件名
        zos.putNextEntry(entry);
        // 向ZIP實(shí)體中寫入內(nèi)容
        byte[] buf = new byte[1024];
        int len;
        while ((len = fis.read(buf)) > 0) {
            zos.write(buf, 0, len);
        }
        // 關(guān)閉ZipEntry
        zos.closeEntry();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

注意:

涉及到文件IO流,如果沒有使用try with source 的語(yǔ)法,一定要記得關(guān)閉輸入輸出流;

使用Java.util.zip下的工具類壓縮成ZIP不支持設(shè)置ZIP密碼且每種模式下生成的ZIP文件大小大于等于原文件/文件夾;

當(dāng)使用STORED壓縮方法時(shí),需要設(shè)置未壓縮的數(shù)據(jù)大小和CRC-32校驗(yàn)和,該值需要借助CRC-32計(jì)算非常的麻煩,不設(shè)置此值會(huì)拋出異常;

對(duì)于壓縮,可以使用 ZipOutputStream 的 putNextEntry 方法逐個(gè)添加文件,避免將所有文件一次性讀入內(nèi)存;

可以設(shè)置緩沖區(qū)大小,例如對(duì)于文件的讀取和寫入,可以設(shè)置緩沖區(qū)大小為 4KB 或者 8KB,減少內(nèi)存的占用;

對(duì)于解壓縮,可以使用 ZipInputStream 的 getNextEntry 方法逐個(gè)讀取文件,避免將所有文件一次性讀入內(nèi)存;

解壓縮ZIP文件

將ZIP文件解壓縮支持生成文件夾,代碼如下:

/**
 * 解壓縮ZIP文件
 * @param zipFile ZIP文件
 * @param destDir 目標(biāo)路徑
 */
public static void zipDecompress(File zipFile, File destDir) {
    byte[] buffer = new byte[1024];
    try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) {
        ZipEntry entry = zis.getNextEntry();
        while (entry != null) {
            File file = new File(destDir, entry.getName());
            if (entry.isDirectory()) {
                file.mkdirs();
            } else {
                File parent = file.getParentFile();
                if (!parent.exists()) {
                    parent.mkdirs();
                }
                try (FileOutputStream fos = new FileOutputStream(file)) {
                    int len;
                    while ((len = zis.read(buffer)) > 0) {
                        fos.write(buffer, 0, len);
                    }
                }
            }
            entry = zis.getNextEntry();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

如何避免壓縮文件中的注入攻擊?

壓縮文件中的路徑名和文件名可以被精心構(gòu)造的攻擊者利用,從而使得解壓縮的過程中可能會(huì)導(dǎo)致路徑遍歷、文件覆蓋等問題,進(jìn)而導(dǎo)致安全問題。為了避免這些安全問題,可以進(jìn)行如下處理:

  • 限制壓縮文件中的路徑名和文件名的長(zhǎng)度,以及字符集,可以采用白名單機(jī)制對(duì)輸入進(jìn)行限制;
  • 對(duì)于解壓縮的路徑名和文件名,不要使用壓縮文件中的路徑名和文件名,而是在解壓縮時(shí)自行構(gòu)造一個(gè)路徑名和文件名;
  • 對(duì)于不可信任的壓縮文件,最好在安全的環(huán)境下解壓縮,例如在沙箱或虛擬機(jī)中進(jìn)行操作。

檢驗(yàn)

主要測(cè)試使用上述代碼壓縮單文件和解壓縮單文件能否成功以后測(cè)量單個(gè)文件的處理耗時(shí),同時(shí)測(cè)試單文件夾多文件壓縮和解壓縮能否成功以及處理耗時(shí),單元測(cè)試代碼如下:

@Test
void testJavaUtilZip(){
    // 測(cè)試壓縮和解壓縮單文件ZIP
    // 被壓縮的MP4單文件,大小112.5MB
    File inputFile = new File("/Users/zlc/Documents/own/images/GPT-4 Developer Livestream.mp4");
    // ZIP文件路徑
    File zipFile = new File("/Users/zlc/Documents/own/mp4.zip");
    // ZIP 解壓縮路徑
    File unzipFile = new File("/Users/zlc/Documents/own/unzip");
    long start = System.currentTimeMillis();
    // 壓縮文件
    ZipFileUtil.zipCompress(inputFile, zipFile);
    long end = System.currentTimeMillis();
    System.out.println("ZIP-壓縮單文件耗時(shí):" + (end - start) + "毫秒");
    start = System.currentTimeMillis();
    ZipFileUtil.zipDecompress(zipFile, unzipFile);
    end = System.currentTimeMillis();
    System.out.println("ZIP-解壓縮單文件耗時(shí):" + (end - start) + "毫秒");
    // 單文件夾多文件壓縮和解壓縮測(cè)試
    // 文件夾大小2.42G
    File inputFiles = new File("/Users/zlc/Documents/own/images");
    File zipFiles = new File("/Users/zlc/Documents/own/imagesZip.zip");
    File unzipFiles = new File("/Users/zlc/Documents/own/imagesUnzip");
    start = System.currentTimeMillis();
    // 壓縮文件
    ZipFileUtil.zipCompress(inputFiles, zipFiles);
    end = System.currentTimeMillis();
    System.out.println("ZIP - 多文件壓縮耗時(shí):" + (end - start) + "毫秒");
    start = System.currentTimeMillis();
    ZipFileUtil.zipDecompress(zipFiles, unzipFiles);
    end = System.currentTimeMillis();
    System.out.println("ZIP - 多文件解壓縮耗時(shí):" + (end - start) + "毫秒");
}

測(cè)試結(jié)果如下:

ZIP-壓縮單文件耗時(shí):5492毫秒
ZIP-解壓縮單文件耗時(shí):1920毫秒
ZIP - 多文件壓縮耗時(shí):136059毫秒
ZIP - 多文件解壓縮耗時(shí):45739毫秒

同時(shí)測(cè)試了設(shè)置不同壓縮等級(jí)的耗時(shí)比較,結(jié)果如下:

// 不同壓縮等級(jí)下的處理耗時(shí) 
BEST_COMPRESSION 2.43G 文件夾耗時(shí)119801毫秒快兩分鐘了
BEST_SPEED 2.43G 文件夾耗時(shí)112646毫秒 也沒差多少,但確實(shí)快了

結(jié)論:

測(cè)試環(huán)境為MacOS 四核Intel Core i7,16G內(nèi)存,處理將近2.5G大小的文件夾耗時(shí)接近兩分鐘,效率十分低下,在總文件大小不大的時(shí)候可以考慮使用JDK自帶的壓縮工具類。

應(yīng)用

設(shè)計(jì)一個(gè)API,通過使用Hutool生成兩個(gè)CSV文件和一個(gè)Excel文件,將這三個(gè)文件放入到ZIP壓縮包中,當(dāng)通過瀏覽器調(diào)用API時(shí),下載ZIP壓縮包。

同時(shí)在服務(wù)器上不生成CVS、EXCEL以及Zip文件而是直接通過HttpServletResponse將文件傳送給客戶端,避免服務(wù)器因過多生成這些文件導(dǎo)致服務(wù)硬盤不夠用(PS:生成文件以后基本上不會(huì)有人管了,會(huì)隨著時(shí)間的增加爆炸式增加,當(dāng)然如果你需要留存建議生成本地文件存儲(chǔ)到OSS中),代碼如下:

  /**
 * 下載ZIP
 * @param response HttpServletResponse 響應(yīng)流
 * @return zip file 
 */
@GetMapping(value = "/downloadZip")
public String downloadZipFile(HttpServletResponse response) {
    // 設(shè)置響應(yīng)頭
    response.setContentType("application/octet-stream");
    response.setHeader("Content-Disposition", "attachment; filename=download.zip");
    try (ZipOutputStream outputStream = new ZipOutputStream(response.getOutputStream())) {
        ExcelWriter writer = new ExcelWriter(true);
        List<String> header = Arrays.asList("開始日期", "結(jié)束日期", "算法廠商", "期末資產(chǎn)總額", "累計(jì)成交金額",
                "期間委托筆數(shù)", "期間成交筆數(shù)", "期間撤單筆數(shù)");
        writer.writeHeadRow(header);
        List<List<String>> rows = new ArrayList<>();
        rows.add(Arrays.asList("張三", "里斯", "男", "張三", "里斯", "男", "張三", "里斯"));
        rows.add(Arrays.asList("李四", "王武", "女", "張三", "里斯", "男", "張三", "里斯"));
        writer.write(rows);
        writer.passRows(1);
        List<String> header1 = Arrays.asList("日期", "資金賬號(hào)", "算法母單編號(hào)", "委托編號(hào)", "交易所", "股票代碼",
                "委托數(shù)量", "交易方向", "訂單類型", "委托價(jià)格", "委托狀態(tài)", "累計(jì)成交數(shù)量", "累計(jì)成交金額");
        writer.writeHeadRow(header1);
        writer.autoSizeColumn(writer.getCurrentRow());
        List<List<String>> rows2 = new ArrayList<>();
        rows2.add(Arrays.asList("張三", "里斯", "男", "張三", "里斯", "男", "張三", "里斯", "找大大", "趙打打", "炸", "茅臺(tái)", "米線"));
        rows2.add(Arrays.asList("李四", "王武", "女", "張三", "里斯", "男", "張三", "里斯", "找大大", "趙打打", "炸", "茅臺(tái)", "米線"));
        writer.write(rows2);
        writer.autoSizeColumnAll();
        ZipEntry entry = new ZipEntry("數(shù)據(jù).xlsx");
        outputStream.putNextEntry(entry);
        writer.flush(outputStream);
        writer.close();
        outputStream.closeEntry();
        ZipEntry entry1 = new ZipEntry("母單.csv");
        outputStream.putNextEntry(entry1);
        CsvWriter csvWriter1 = new CsvWriter(new OutputStreamWriter(outputStream));
        String[] csvHead1 = {"日期", "資金賬號(hào)", "算法廠商", "算法", "算法母單編號(hào)", "交易所", "股票代碼", "委托數(shù)量", "交易方向", "啟動(dòng)時(shí)間", "停止時(shí)間", "算法狀態(tài)"};
        csvWriter1.writeLine(csvHead1);
        String[] csvData1 = {"20230203", "98830901", "XX", "TWAP", "12984", "SH", "600000", "10000", "4", "102311", "112311", "1"};
        String[] csvData2 = {"20230203", "98830901", "XX", "TWAP", "12984", "SH", "600000", "10000", "4", "102311", "112311", "1"};
        csvWriter1.writeLine(csvData1);
        csvWriter1.writeLine(csvData2);
        csvWriter1.flush();
        outputStream.closeEntry();
        ZipEntry entry2 = new ZipEntry("子單.csv");
        outputStream.putNextEntry(entry2);
        CsvWriter csvWriter2 = new CsvWriter(new OutputStreamWriter(outputStream));
        String[] csvHead2 = {"日期", "資金賬號(hào)", "算法母單編號(hào)", "委托編號(hào)", "交易所", "股票代碼", "委托數(shù)量", "交易方向", "訂單類型", "委托價(jià)格", "委托狀態(tài)", "累計(jì)成交數(shù)量", "累計(jì)成交金額"};
        csvWriter2.writeLine(csvHead2);
        String[] csvData3 = {"20230203", "98830901", "12984", "ord1122", "SH", "600000", "1000", "4", "1", "7.23", "1", "800", "5600"};
        String[] csvData4 = {"20230203", "98830901", "12984", "ord1122", "SH", "600000", "1000", "4", "1", "7.23", "1", "800", "5600"};
        csvWriter2.writeLine(csvData3);
        csvWriter2.writeLine(csvData4);
        csvWriter2.flush();
        outputStream.closeEntry();
        outputStream.finish();
        return "success";
    } catch (Exception e) {
        return "faild";
    }
}

總結(jié)

Java 自帶的 java.util.zip 類庫(kù)是一個(gè)基礎(chǔ)的壓縮和解壓縮類庫(kù),它提供了很基本的壓縮和解壓縮功能。在處理小型文件或數(shù)據(jù)時(shí),java.util.zip 是一個(gè)可行的選擇。不過,對(duì)于大型文件或數(shù)據(jù)的處理,效率可能會(huì)受到影響。相比之下,一些第三方的類庫(kù)如 Apache Commons Compress、Zip4j 等提供了更為高級(jí)的壓縮和解壓縮功能,同時(shí)也提供了更好的性能。

到此這篇關(guān)于Java壓縮和解壓縮ZIP文件的文章就介紹到這了,更多相關(guān)Java壓縮和解壓縮ZIP文件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JAVA中 Spring定時(shí)器的兩種實(shí)現(xiàn)方式

    JAVA中 Spring定時(shí)器的兩種實(shí)現(xiàn)方式

    本文向您介紹Spring定時(shí)器的兩種實(shí)現(xiàn)方式,包括Java Timer定時(shí)和Quartz定時(shí)器,兩種Spring定時(shí)器的實(shí)現(xiàn)方式各有優(yōu)點(diǎn),可結(jié)合具體項(xiàng)目考慮是否采用。
    2015-09-09
  • Java代碼里如何拼接SQL語(yǔ)句到mybatis的xml

    Java代碼里如何拼接SQL語(yǔ)句到mybatis的xml

    這篇文章主要介紹了Java代碼里拼接SQL語(yǔ)句到mybatis的xml操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Java中的setting和getting使用方法

    Java中的setting和getting使用方法

    為了保障數(shù)據(jù)的安全性,通常將數(shù)據(jù)成員定義為private(封裝或私有化),這樣外部代碼就無(wú)法直接訪問這些數(shù)據(jù),只能通過類提供的公共方法來進(jìn)行訪問,這種方法主要包括setter和getter方法,以及構(gòu)造方法,setter方法用于給私有屬性賦值
    2024-09-09
  • JavaWeb監(jiān)聽器Listener實(shí)例解析

    JavaWeb監(jiān)聽器Listener實(shí)例解析

    這篇文章主要為大家詳細(xì)介紹了JavaWeb監(jiān)聽器Listener實(shí)例,針對(duì)監(jiān)聽器進(jìn)行進(jìn)行細(xì)致分析,感興趣的小伙伴們可以參考一下
    2016-08-08
  • SpringMvc @Valid如何拋出攔截異常

    SpringMvc @Valid如何拋出攔截異常

    這篇文章主要介紹了SpringMvc @Valid如何拋出攔截異常,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • IntelliJ Idea 2020.1 正式發(fā)布,官方支持中文(必看)

    IntelliJ Idea 2020.1 正式發(fā)布,官方支持中文(必看)

    這篇文章主要介紹了IntelliJ Idea 2020.1 正式發(fā)布,官方支持中文了,本文通過截圖的形式給大家展示,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-04-04
  • springcloud項(xiàng)目快速開始起始模板的實(shí)現(xiàn)

    springcloud項(xiàng)目快速開始起始模板的實(shí)現(xiàn)

    本文主要介紹了springcloud項(xiàng)目快速開始起始模板思路的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • java集合繼承關(guān)系圖分享

    java集合繼承關(guān)系圖分享

    這篇文章主要為大家詳細(xì)介紹了java集合繼承關(guān)系圖,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • Maven私服倉(cāng)庫(kù)Nexus配置小結(jié)

    Maven私服倉(cāng)庫(kù)Nexus配置小結(jié)

    Maven 私服是一種特殊的Maven遠(yuǎn)程倉(cāng)庫(kù),它是架設(shè)在局域網(wǎng)內(nèi)的倉(cāng)庫(kù)服務(wù),本文就來介紹一下Maven私服倉(cāng)庫(kù)Nexus配置小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-08-08
  • 深入了解SpringAOP中的jdk動(dòng)態(tài)代理與CGlib

    深入了解SpringAOP中的jdk動(dòng)態(tài)代理與CGlib

    這篇文章主要介紹了深入了解SpringAOP中的jdk動(dòng)態(tài)代理與CGlib,一般我們編寫程序的思想是縱向的,也就是一個(gè)方法代碼從該方法第一行開始往下一步一步走,直到走完最后一行代碼,也就是說很多業(yè)務(wù)都需要的比如用戶鑒權(quán),資源釋放等,需要的朋友可以參考下
    2023-12-12

最新評(píng)論