使用apache.commons.compress对文件进行压缩与解压缩的不同方式

使用apache.commons.compress对文件进行压缩与解压缩的不同方式

依赖
		<dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-compress</artifactId>
            <version>1.21</version>
        </dependency>
话不多说,直接上代码
package com.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.compressors.lz4.FramedLZ4CompressorInputStream;
import org.apache.commons.compress.compressors.lz4.FramedLZ4CompressorOutputStream;
import org.apache.commons.compress.compressors.snappy.FramedSnappyCompressorInputStream;
import org.apache.commons.compress.compressors.snappy.FramedSnappyCompressorOutputStream;
import org.apache.commons.compress.utils.IOUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Objects;
import java.util.zip.*;

/**
 * 公用压缩工具类,支持Gzip、Zip、Tar、LZ4、Snappy、Deflater压缩算法
 *
 * @description:
 * @author: 
 * @date: 2024/03/29 21:40
 */
@Slf4j
public class CommonsCompressUtil {

    public static class Gzip {
        /**
         * 将文件压缩成gzip
         *
         * @param sourceFile 源文件,如:archive.tar
         * @return 输入流
         */
        public static InputStream compress(byte[] sourceFile) {
            long d1 = System.currentTimeMillis();
            ByteArrayOutputStream compressedOutput = new ByteArrayOutputStream();
            try {
                ByteArrayInputStream sourceInput = new ByteArrayInputStream(sourceFile);
                GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(compressedOutput);

                int buffersize = 10240;
                final byte[] buffer = new byte[buffersize];
                int n;
                while (-1 != (n = sourceInput.read(buffer))) {
                    gzOut.write(buffer, 0, n);
                }
                gzOut.finish(); // 确保所有数据都被压缩并刷新到底层输出流

            } catch (IOException e) {
                log.info("压缩失败,原因:{}", e.getMessage());
            }
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedOutput.toByteArray());
            log.info("压缩完毕,耗时:{}ms,", (System.currentTimeMillis() - d1));
            return byteArrayInputStream;
        }

        /**
         * 将gzip文件解压缩
         *
         * @param sourceFile 源文件,如:archive.tar.gzip
         * @return 输入流
         */
        public static InputStream uncompress(byte[] sourceFile) {
            long d1 = System.currentTimeMillis();
            ByteArrayInputStream compressedInput = new ByteArrayInputStream(sourceFile);
            ByteArrayOutputStream decompressedOutput = new ByteArrayOutputStream();
            try {
                GzipCompressorInputStream gzIn = new GzipCompressorInputStream(compressedInput);
                byte[] buffer = new byte[1024];
                int n;
                while (-1 != (n = gzIn.read(buffer))) {
                    decompressedOutput.write(buffer, 0, n);
                }
            } catch (Exception e) {
                log.info("解压缩失败,原因:{}", e.getMessage());
            }
            // 返回 ByteArrayInputStream 包装的解压数据
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decompressedOutput.toByteArray());
            log.info("压缩完毕,耗时:{}ms,", (System.currentTimeMillis() - d1));
            return byteArrayInputStream;
        }
    }

    public static class LZ4 {
        /**
         * 将文件压缩成LZ4文件
         *
         * @param sourceFile 源文件,如:archive.tar
         */
        public static InputStream compress(byte[] sourceFile) {
            long d1 = System.currentTimeMillis();
            ByteArrayInputStream compressedInput = new ByteArrayInputStream(sourceFile);
            ByteArrayOutputStream decompressedOutput = new ByteArrayOutputStream();
            try (
                    FramedLZ4CompressorOutputStream lzOut = new FramedLZ4CompressorOutputStream(decompressedOutput);) {
                int buffersize = 10240;
                final byte[] buffer = new byte[buffersize];
                int n = 0;
                while (-1 != (n = compressedInput.read(buffer))) {
                    lzOut.write(buffer, 0, n);
                }
            } catch (IOException e) {
                log.info("压缩失败,原因:{}", e.getMessage());
            }
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decompressedOutput.toByteArray());
            log.info("压缩完毕,耗时:{}ms,", (System.currentTimeMillis() - d1));
            return byteArrayInputStream;
        }

        /**
         * 将LZ4文件进行解压
         *
         * @param sourceFile 源文件,如:archive.tar.lz4
         */
        public static InputStream uncompress(byte[] sourceFile) {
            long d1 = System.currentTimeMillis();
            ByteArrayInputStream compressedInput = new ByteArrayInputStream(sourceFile);
            ByteArrayOutputStream decompressedOutput = new ByteArrayOutputStream();
            try (
                    FramedLZ4CompressorInputStream zIn = new FramedLZ4CompressorInputStream(compressedInput);) {
                int buffersize = 10240;
                final byte[] buffer = new byte[buffersize];
                int n = 0;
                while (-1 != (n = zIn.read(buffer))) {
                    decompressedOutput.write(buffer, 0, n);
                }
            } catch (IOException e) {
                log.info("解压缩失败,原因:{}", e.getMessage());
            }
            // 返回 ByteArrayInputStream 包装的解压数据
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decompressedOutput.toByteArray());
            log.info("压缩完毕,耗时:{}ms,", (System.currentTimeMillis() - d1));
            return byteArrayInputStream;
        }
    }

    public static class Snappy {
        /**
         * 将文件压缩成sz
         *
         * @param sourceFile 源文件,如:archive.tar
         */
        public static InputStream compress(byte[] sourceFile) {
            long d1 = System.currentTimeMillis();
            ByteArrayInputStream compressedInput = new ByteArrayInputStream(sourceFile);
            ByteArrayOutputStream decompressedOutput = new ByteArrayOutputStream();
            try (
                    FramedSnappyCompressorOutputStream snOut = new FramedSnappyCompressorOutputStream(decompressedOutput);) {
                int buffersize = 10240;
                final byte[] buffer = new byte[buffersize];
                int n = 0;
                while (-1 != (n = compressedInput.read(buffer))) {
                    snOut.write(buffer, 0, n);
                }
            } catch (IOException e) {
                log.info("压缩失败,原因:{}", e.getMessage());
            }
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decompressedOutput.toByteArray());
            log.info("压缩完毕,耗时:{}ms,", (System.currentTimeMillis() - d1));
            return byteArrayInputStream;
        }

        /**
         * 将sz文件解压
         *
         * @param sourceFile 源文件,如:archive.tar.sz
         */
        public static InputStream uncompress(byte[] sourceFile) {
            long d1 = System.currentTimeMillis();
            ByteArrayInputStream compressedInput = new ByteArrayInputStream(sourceFile);
            ByteArrayOutputStream decompressedOutput = new ByteArrayOutputStream();
            try (
                    FramedSnappyCompressorInputStream zIn = new FramedSnappyCompressorInputStream(compressedInput);) {
                int buffersize = 10240;
                final byte[] buffer = new byte[buffersize];
                int n = 0;
                while (-1 != (n = zIn.read(buffer))) {
                    decompressedOutput.write(buffer, 0, n);
                }
            } catch (IOException e) {
                log.info("解压缩失败,原因:{}", e.getMessage());
            }
            // 返回 ByteArrayInputStream 包装的解压数据
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decompressedOutput.toByteArray());
            log.info("压缩完毕,耗时:{}ms,", (System.currentTimeMillis() - d1));
            return byteArrayInputStream;
        }
    }

    public static class Zip {
        /**
         * 将文件压缩成zip
         *
         * @param sourceFile 源文件或目录,如:archive.tar
         * @param targetFile 目标文件,如:archive.tar.zip
         */
        public static void compress(String sourceFile, String targetFile) {
            long d1 = System.currentTimeMillis();
            ByteArrayOutputStream fos = new ByteArrayOutputStream();
            ZipArchiveOutputStream bos = new ZipArchiveOutputStream(fos);
            try (ArchiveOutputStream aos = new ZipArchiveOutputStream(bos);) {
                Path dirPath = Paths.get(sourceFile);
                Files.walkFileTree(dirPath, new SimpleFileVisitor<Path>() {
                    @Override
                    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                        ArchiveEntry entry = new ZipArchiveEntry(dir.toFile(), dirPath.relativize(dir).toString());
                        aos.putArchiveEntry(entry);
                        aos.closeArchiveEntry();
                        return super.preVisitDirectory(dir, attrs);
                    }

                    @Override
                    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                        ArchiveEntry entry = new ZipArchiveEntry(
                                file.toFile(), dirPath.relativize(file).toString());
                        aos.putArchiveEntry(entry);
                        IOUtils.copy(new FileInputStream(file.toFile()), aos);
                        aos.closeArchiveEntry();
                        return super.visitFile(file, attrs);
                    }
                });
            } catch (IOException e) {
                System.out.println("压缩失败,原因:" + e.getMessage());
            }
            System.out.println("压缩完毕,耗时:" + (System.currentTimeMillis() - d1) + " ms");
        }

        /**
         * 将zip文件解压到指定目录
         *
         * @param zipPath 源文件,如:archive.zip
         * @param descDir 解压目录
         */
        public static void uncompress(String zipPath, String descDir) {
            long d1 = System.currentTimeMillis();
            try (InputStream fis = Files.newInputStream(Paths.get(zipPath));
                 InputStream bis = new BufferedInputStream(fis);
                 ArchiveInputStream ais = new ZipArchiveInputStream(bis);
            ) {
                ArchiveEntry entry;
                while (Objects.nonNull(entry = ais.getNextEntry())) {
                    if (!ais.canReadEntryData(entry)) {
                        continue;
                    }
                    String name = descDir + File.separator + entry.getName();
                    File f = new File(name);
                    if (entry.isDirectory()) {
                        if (!f.isDirectory() && !f.mkdirs()) {
                            f.mkdirs();
                        }
                    } else {
                        File parent = f.getParentFile();
                        if (!parent.isDirectory() && !parent.mkdirs()) {
                            throw new IOException("failed to create directory " + parent);
                        }
                        try (OutputStream o = Files.newOutputStream(f.toPath())) {
                            IOUtils.copy(ais, o);
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("解压失败,原因:" + e.getMessage());
            }
            System.out.println("解压完毕,耗时:" + (System.currentTimeMillis() - d1) + " ms");
        }

    }

    public static class Tar {
        /**
         * 将文件压缩成tar
         *
         * @param sourceFile 源文件或目录,如:archive
         * @param targetFile 目标文件,如:archive.tar
         */
        public static void compress(String sourceFile, String targetFile) {
            long d1 = System.currentTimeMillis();
            try (OutputStream fos = new FileOutputStream(targetFile);
                 OutputStream bos = new BufferedOutputStream(fos);
                 ArchiveOutputStream aos = new TarArchiveOutputStream(bos);) {
                Path dirPath = Paths.get(sourceFile);
                Files.walkFileTree(dirPath, new SimpleFileVisitor<Path>() {
                    @Override
                    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                        ArchiveEntry entry = new TarArchiveEntry(dir.toFile(), dirPath.relativize(dir).toString());
                        aos.putArchiveEntry(entry);
                        aos.closeArchiveEntry();
                        return super.preVisitDirectory(dir, attrs);
                    }

                    @Override
                    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                        ArchiveEntry entry = new TarArchiveEntry(
                                file.toFile(), dirPath.relativize(file).toString());
                        aos.putArchiveEntry(entry);
                        IOUtils.copy(new FileInputStream(file.toFile()), aos);
                        aos.closeArchiveEntry();
                        return super.visitFile(file, attrs);
                    }
                });
            } catch (IOException e) {
                System.out.println("压缩失败,原因:" + e.getMessage());
            }
            System.out.println("压缩完毕,耗时:" + (System.currentTimeMillis() - d1) + " ms");
        }

        /**
         * 将tar文件解压到指定目录
         *
         * @param tarPath 源文件,如:archive.tar
         * @param descDir 解压目录
         */
        public static void uncompress(String tarPath, String descDir) {
            long d1 = System.currentTimeMillis();
            try (InputStream fis = Files.newInputStream(Paths.get(tarPath));
                 InputStream bis = new BufferedInputStream(fis);
                 ArchiveInputStream ais = new TarArchiveInputStream(bis);
            ) {
                ArchiveEntry entry;
                while (Objects.nonNull(entry = ais.getNextEntry())) {
                    if (!ais.canReadEntryData(entry)) {
                        continue;
                    }
                    String name = descDir + File.separator + entry.getName();
                    File f = new File(name);
                    if (entry.isDirectory()) {
                        if (!f.isDirectory() && !f.mkdirs()) {
                            f.mkdirs();
                        }
                    } else {
                        File parent = f.getParentFile();
                        if (!parent.isDirectory() && !parent.mkdirs()) {
                            throw new IOException("failed to create directory " + parent);
                        }
                        try (OutputStream o = Files.newOutputStream(f.toPath())) {
                            IOUtils.copy(ais, o);
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("解压失败,原因:" + e.getMessage());
            }
            System.out.println("解压完毕,耗时:" + (System.currentTimeMillis() - d1) + " ms");
        }
    }



    public static class Deflaters {
   
        public static InputStream compress(byte[] data) throws IOException {
            // 创建一个ByteArrayOutputStream来存储压缩后的数据
            ByteArrayOutputStream compressedDataStream = new ByteArrayOutputStream();

            // 创建一个DeflaterOutputStream来压缩数据
            // 这里使用了最佳压缩级别(Deflater.BEST_COMPRESSION)
            try (DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(compressedDataStream, new Deflater(Deflater.BEST_COMPRESSION))) {
                // 将数据写入DeflaterOutputStream进行压缩
                deflaterOutputStream.write(data);
                // 完成压缩操作
                deflaterOutputStream.finish();
                // 刷新输出流,确保所有数据都已经被写出
                deflaterOutputStream.flush();
            }

            // 将压缩后的数据封装为ByteArrayInputStream,这样就可以作为InputStream使用
            InputStream compressedInputStream = new ByteArrayInputStream(compressedDataStream.toByteArray());

            // 返回压缩后的InputStream
            return compressedInputStream;
        }

        public static InputStream uncompress(byte[] compressedData) throws IOException {
            // 创建一个ByteArrayOutputStream来存储解压缩后的数据
            ByteArrayOutputStream decompressedDataStream = new ByteArrayOutputStream();

            // 创建一个InflaterInputStream来解压缩数据
            try (InflaterInputStream inflaterInputStream = new InflaterInputStream(new ByteArrayInputStream(compressedData), new Inflater())) {
                // 创建一个缓冲区来读取解压缩的数据
                byte[] buffer = new byte[1024];
                int len;
                // 读取解压缩的数据到缓冲区,直到没有更多的数据
                while ((len = inflaterInputStream.read(buffer)) > 0) {
                    // 将缓冲区的数据写入ByteArrayOutputStream
                    decompressedDataStream.write(buffer, 0, len);
                }
            }

            // 将解压缩后的数据封装为ByteArrayInputStream,这样就可以作为InputStream使用
            InputStream decompressedInputStream = new ByteArrayInputStream(decompressedDataStream.toByteArray());

            // 返回解压缩后的InputStream
            return decompressedInputStream;
        }
    }


}

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 以下是使用 Apache Commons Compress 库实现zip文件分卷压缩的示例代码: ```java import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import org.apache.commons.compress.utils.IOUtils; public class ZipUtils { public static void zipSplit(File inputFile, int splitSize) throws IOException { BufferedInputStream bis = null; FileInputStream fis = null; ZipArchiveOutputStream zos = null; try { fis = new FileInputStream(inputFile); bis = new BufferedInputStream(fis); byte[] buffer = new byte[1024]; int read = 0; int partNum = 0; while ((read = bis.read(buffer)) != -1) { String partName = inputFile.getName() + ".part" + partNum + ".zip"; File partFile = new File(inputFile.getParentFile(), partName); ZipArchiveEntry zipEntry = new ZipArchiveEntry(inputFile.getName()); zipEntry.setSize(read); zos = new ZipArchiveOutputStream(new BufferedOutputStream(new FileOutputStream(partFile))); zos.putArchiveEntry(zipEntry); zos.write(buffer, 0, read); zos.closeArchiveEntry(); zos.close(); partNum++; } } finally { IOUtils.closeQuietly(bis); IOUtils.closeQuietly(fis); IOUtils.closeQuietly(zos); } } } ``` 此示例使用 `ZipArchiveOutputStream` 类创建分卷 zip 文件,并将输入文件分割成固定大小的块。在此示例中,文件将被分割成大小为 `splitSize` 的块。 示例中的 `zipSplit` 方法接受两个参数:`inputFile`,它是要分卷压缩文件,以及 `splitSize`,它是每个分卷文件的大小。 请注意,此示例仅创建 zip 文件的分卷,而不进行解压缩或合并。如果要解压或合并分卷 zip 文件,请使用 `ZipArchiveInputStream` 类。 ### 回答2: Apache Commons Compress 是一个用于处理各种压缩和存档格式的 Java 库。它提供了一系列实用程序和类,用于创建、读取和操作各种压缩格式的文件。 下面是一个使用 Apache Commons Compress 实现 zip 文件分卷压缩的代码示例: ```java import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import java.io.*; public class ZipVolumeExample { private static final int CHUNK_SIZE = 1024 * 1024; // 每个分卷的大小设置为1MB public static void main(String[] args) { String sourceFilePath = "path/to/source/file.txt"; String destinationFolderPath = "path/to/destination/folder"; String destinationBaseFileName = "output"; File sourceFile = new File(sourceFilePath); File destinationFolder = new File(destinationFolderPath); if (!sourceFile.exists()) { System.out.println("Source file does not exist!"); return; } if (!destinationFolder.exists() || !destinationFolder.isDirectory()) { System.out.println("Destination folder does not exist!"); return; } try (InputStream inputStream = new FileInputStream(sourceFile)) { int volumeCount = 1; int bytesRead; byte[] buffer = new byte[CHUNK_SIZE]; ZipArchiveOutputStream zipOutputStream = null; while ((bytesRead = inputStream.read(buffer)) > 0) { String volumeFileName = String.format("%s_%03d.zip", destinationBaseFileName, volumeCount++); File volumeFile = new File(destinationFolder, volumeFileName); zipOutputStream = new ZipArchiveOutputStream(new FileOutputStream(volumeFile)); ZipArchiveEntry zipEntry = new ZipArchiveEntry(sourceFile.getName()); zipOutputStream.putArchiveEntry(zipEntry); zipOutputStream.write(buffer, 0, bytesRead); zipOutputStream.closeArchiveEntry(); zipOutputStream.close(); } if (zipOutputStream != null) { zipOutputStream.close(); } System.out.println("Zip volume compression completed!"); } catch (IOException e) { e.printStackTrace(); } } } ``` 上述代码示例中,我们通过指定源文件路径和目标文件夹路径来定义要进行分卷压缩文件以及输出文件夹。代码中的 CHUNK_SIZE 变量定义了每个分卷的大小,这里设置为 1MB。然后,我们通过循环读取源文件,并将数据写入分卷的 zip 文件中。 在循环过程中,我们使用 ZipArchiveOutputStream 类创建一个新的 zip 输出流,并将源文件的内容写入到该流中。每当达到 CHUNK_SIZE 大小时,我们就关闭当前的分卷 zip 文件,并为下一个分卷创建一个新的 zip 输出流。最后,我们在循环结束后关闭输出流。 通过使用 Apache Commons Compress 提供的 ZipArchiveOutputStream 类,我们能够轻松实现 zip 文件的分卷压缩。这种方法可以将大型文件分割成多个块,方便传输和存储。 ### 回答3: Apache Commons Compress是一个开源的Java库,用于处理各种压缩解压缩格式的文件。它提供了丰富的功能,包括对zip文件的创建、更新、解压缩等操作。以下是一个使用Apache Commons Compress实现zip文件分卷压缩的代码示例: ```java import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class ZipSplitExample { public static void main(String[] args) throws IOException { String inputFilePath = "path/to/input/file.zip"; String outputDirectory = "path/to/output/directory/"; int maxFileSize = 10 * 1024 * 1024; // 10 MB File inputFile = new File(inputFilePath); FileInputStream fis = new FileInputStream(inputFile); byte[] buffer = new byte[maxFileSize]; int partNum = 1; int bytesRead; while ((bytesRead = fis.read(buffer)) > 0) { String outputFilePath = outputDirectory + "part" + partNum + ".zip"; FileOutputStream fos = new FileOutputStream(outputFilePath); ZipArchiveOutputStream zipOutput = new ZipArchiveOutputStream(fos); ZipArchiveEntry entry = new ZipArchiveEntry("part" + partNum + ".bin"); zipOutput.putArchiveEntry(entry); zipOutput.write(buffer, 0, bytesRead); zipOutput.closeArchiveEntry(); zipOutput.finish(); zipOutput.close(); fos.close(); partNum++; } fis.close(); } } ``` 上述代码中,首先需要指定输入的zip文件路径(inputFilePath),以及输出分卷压缩文件的目录(outputDirectory)。参数maxFileSize用于指定每个分卷的最大文件大小(此处设置为10MB)。 代码会从输入zip文件中读取数据,并按照每个分卷的最大文件大小将数据写入到输出文件中。每个分卷的文件名通过添加后缀"part"和分卷编号来表示。 需要注意的是,代码中虽然只演示了对zip文件的分卷压缩操作,但Apache Commons Compress还提供了丰富的其他功能,如解压缩、添加、删除文件等操作,可以根据具体需求进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值