精华内容
下载资源
问答
  • java压缩图片文件大小

    热门讨论 2014-05-14 11:10:20
    java 开发压缩图片文件大小,2m-->200k 不是压缩宽高的
  • 文件压缩压缩

    千次阅读 2018-09-10 19:12:15
    脑子一热,想到再次压缩文件?没有百度到,想要的答案,自己费事来try try。 看不懂的知识:https://blog.csdn.net/xuchuangqi/article/details/52939705 gzip 对于要压缩的文件,首先使用LZ77算法的一个...

    背景:看到文件压缩gzip,bzip2。脑子一热,想到能不能再次压缩文件?没有百度到,想要的答案,自己费事来try try。

    看不懂的知识:https://blog.csdn.net/xuchuangqi/article/details/52939705

    gzip 对于要压缩的文件,首先使用LZ77算法的一个变种进行压缩,对得到的结果再使用Huffman编码的方法(实际上gzip根据情况,选择使用静态Huffman编码或者动态Huffman编码,详细内容在实现中说明)进行压缩。所以明白了LZ77算法和Huffman编码的压缩原理,也就明白了gzip的压缩原理。我们来对LZ77算法和Huffman编码做一个简单介绍。
     

    LZ77算法的压缩原理:使用滑动窗口寻找匹配串

    LZ77从文件的开始处开始,一个字节一个字节的向后进行处理。一个固定大小的窗口(在当前处理字节之前,并且紧挨着当前处理字节),随着处理的字节不断的向后滑动,就象在阳光下,飞机的影子滑过大地一样。对于文件中的每个字节,用当前处理字节开始的串,和窗口中的每个串进行匹配,寻找最长的匹配串。窗口中的每个串指,窗口中每个字节开始的串。如果当前处理字节开始的串在窗口中有匹配串,就用(之间的距离,匹配长度) 这样一对信息,来替换当前串,然后从刚才处理完的串之后的下一个字节,继续处理。如果当前处理字节开始的串在窗口中没有匹配串,就不做改动的输出当前处理字节。

     

    Huffman编码使用Huffman树来产生编码:统计每个符号(我们把字节的256种值看作是256种符号)的出现次数。然后根据符号的出现次数,建立Huffman树,通过Huffman树得到每个符号的新的编码。对于文件中出现次数较多的符号,它的Huffman编码的位数比较少。对于文件中出现次数较少的符号,它的Huffman编码的位数比较多。然后把文件中的每个字节替换成他们新的编码。

     

     正文:

    压缩再压缩

    一、单个文件的压缩再压缩

    1、能否两次使用相同的命令进行压缩?

    2、能否两次使用不同的命令进行交替压缩?

    3、在上面的基础上 ,能否进行n次压缩?

    二、目录文件的压缩

    4、关心的是,目录中含有压缩文件,先打包目录文件,再压缩,目录内部的压缩文件的情况。

     

    try 问题1:能否两次使用相同的命令进行压缩?

    思路:在/tmp下建立一个目录testing1,目录中vim一个文件try1,用gzip命令连续最大程度压缩try1两次

    上面说明,可以两次压缩。第一次压缩97.4%,文件大小由545byte变成45byte,文件变小。

                       第二次压缩11.1%,大小从45byte变成74byte,文件总体变大。

    它们在图形界面下是这个样子。那压缩两次的文件是不是解压两次,才能变为原文件了?

     

    种什么因故,得什么果。上面显示,压缩两次的文件,需要解压两次才行。

     

    同样的方法,我们再试试能否用bzip2命令进行两次压缩。

    这就挺有意思的, bzip2能压缩一次,压缩比为88.99%。但是再次压缩的时候,显示.bz2已经压缩过了。

    那这是不是说,这就证明bzip2不能进行两次压缩呢?

    好像并不能,咋们要是第一次压缩的时候,压缩出来的文件命名不含有.bzip,它能否接着压缩了?try try。

    不急,咋们先进入图形界面中看一看,会发现一些好玩的现象

    这里并不是再次压缩,try.bz2.bz2只是一个空壳子,里面没有东西。咋们验证一下。

    好,咋们把上次的bzip2压缩的文件删除,通过bzip2命令把try压缩成trybz2。再将 trybz2一次压缩文件,用bzip2命令再次压缩。

    上面说明,bzip2也能压缩文件两次。但是有.bz2后缀名的文件,无法用bzip2再次压缩。

     

    try问题2:能否两次使用不同的命令进行交替压缩?

     结果当然也是可以的,即能使用不同的命令进行交替压缩。

    那能否用bzi2压缩try.gz.bz2.gz呢?即try.gz.bz2.gz算不算含有.bz2?      答案是:try.gz.bz2.gz不算含有.bz2

    我觉得上面已经够智障的,压缩n的事情就智障透顶了。丫的,花的时间有点多。

     

    try问题4:关心的是,目录中含有压缩文件,先打包,再压缩,目录内部的压缩文件情况。

    咋们先来猜一猜答案:我猜测,不会压缩打包文件中已经压缩的文件了。

    实验是检验真理的标准。

    首先是删除不必要的文件

    再打包,压缩

    但是,我也不知道,这包里的压缩文件有没有再次压缩。因为里面压缩和外面的压缩方式不同。

    这是一个很有意思也很有价值的问题:目录中含有压缩文件,先打包,再压缩,内部的压缩文件会不会再次被压缩

    这个问题留着,现在我解决不了。因为像bzip2那样,能认出自己已经压缩过的文件不奇怪。

    但bzip2能不能识别出其他方式压缩出来的文件,并避免再次压缩呢?

    这个问题留着,说不定,以后就知道了。(所有的压缩文件在前方有个统一的标注?)

     

    展开全文
  • HDFS文件压缩

    千次阅读 2020-01-04 18:06:11
    在这种情况下,MapReduce会采用正确的做法,它不会尝试切分gzip压缩文件,因为它知道输入的时gzip压缩文件(通过文件扩展名看出)且gzip不持支切分。这是可行的,但 牺牲了数据的本地性 :一个map任务处理8个HDFS块...

    前言

    新搭建的集群,需要进行各种测试,于是让一个同事导入一些测试数据,大约1.4T左右。我进行了一个简单的hive测试

    //故意增加shuffle
    SELECT DISTINCT(COUNT id) FROM test_table;
    

    谁知道…就这么一个简单的SQL,半个小时过去后,仍然没有出结果

    追查原因

    原来同事从ftp拉取过来的数据是gzip格式的,他直接导入到Hive表中…,而gzip格式的数据是不可切分的

    如何理解不可切分

    为了进一步理解gzip等不可切分的压缩格式,我做了个测试

    • 上传测试数据
      在这里插入图片描述
      说明:
      使用linux自带的gzip将testdata(拷贝了bigtable的数据,因为直接gzip bigtable,会删除原始数据)
      或者使用:
      在这里插入图片描述
      说明:
      这样不但可以将原有的文件保留,而且可以将压缩包放到任何目录中,解压缩也一样可以看到bigtable.gz和testdata.gz大小一致

    • 在hdfs根目录创建一个data目录、

    [root@node02 data]# hadoop fs -mkdir /data
    [root@node02 data]# hadoop fs -ls /
    Found 2 items
    drwxr-xr-x   - root supergroup          0 2020-01-04 05:47 /data
    drwxrwxrwx   - root supergroup          0 2019-06-26 06:23 /tmp
    
    • 上传压缩后的数据
      由于数据太小(大约50M),仅有一个block,为了测试,自定义block块为10M:
    hadoop fs -D dfs.blocksize=10485760 -put bigtable.gz /data
    

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    从上图中明显可以看到:gzip格式的数据,不支持切分的真正含义,并不是说HDFS不会将文件分布式的存储在各个节点,而是在计算的时候,不支持切分,也就是仅仅有一个split,从而也就是仅有一个map,这样的效率是及其低下的。
    (后面还会更详细的讲解)
    正常支持切分的数据,执行流程如下:

    在这里插入图片描述
    而不持支切分的数据,如gzip格式的数据,就只有一个map:
    在这里插入图片描述
    这样直接导致的结果就是:
    一个map任务处理N个HDFS块,而其中大多数块并没有存储在执行该map任务的节点上,执行的效率会非常低

    既然这样,难道压缩数据就没有意义了吗?当然不是,而且在生产环境中数据都是经过压缩处理的,接下来详细讲解HDFS数据的压缩

    详解HDFS文件压缩

    • 文件压缩的两大好处
      (1)减少存储文件所需要的磁盘空间
      (2)加速数据在网络和磁盘上的传输

    • 与Hadoop结合使用的常见压缩方法

    压缩格式工具算法文件扩展名是否可切分
    DEFLATEDEFLATE.deflate
    gzipgzipDEFLATE.gz
    bzip2bzip2bzip2.bz2
    LZOlzopLZO.lzo
    LZ4LZ4.lz4
    SnappySnappy.snappy

    说明:

    1. DEFLATE是一个标准压缩算法,该算法的标准实现是zlib。没有可用于生成DEFLATE文件的常用命令行工具,因为通常都用gzip格式。注意,gzip文件格式只是在DEFLATE格式上增加了文件头和一个文件尾。.deflate扩展名是Hadoop约定的。
    2. 如果LZO文件已经在预处理过程中被索引了,那么LZO文件是可切分的。
    • 详细描述文件的压缩
      所有压缩算法都需要权衡空间/时间:压缩和解压缩速度更快,其代价通常是只能节省少量的空间。上表列出所有压缩工具都提供9个不同选项来控制压缩时必须考虑的权衡:选项-1为优化压缩速度,-9为优化压缩空间。例如,下述命令通过最快压缩方法创建一个名为file.gz的压缩文件:
      gzip -1 file
      不同压缩工具有不同的压缩特性。gzip是一个通用的压缩工具,在空间/时间性能的权衡中,属于其他两个压缩方法之间。bzip2的压缩能力强于gzip(即省空间),但压缩速度更慢一点。尽管gzip的解压速度比要锁速度快,但仍比其他压缩格式要慢一些。另一方面,LZO、LZ4和Snappy均优化压缩速度,其速度比gzip快一个数据量级,但压缩效率稍逊一筹。Snappy和LZ4的解压缩速度比LZO高出很多,通过下面的一张图更形象的看出:压缩比和压缩速度是相反的
      在这里插入图片描述
      上表中的“是否可切分”列表示对应的压缩算法是否支持切分(splitable),也就是说,是否可以搜素数据流的任意位置并进一步往下读取数据。可切分压缩格式尤其适合MapReduce(这个原因上面有讲解,下面进行更详细的描述)

    更详细的描述压缩和输入分片

    在考虑如何压缩将有MapReduce处理的数据时,理解这些压缩格式是否支持切分(splitable)是非常重要的。以一个存储在HDFS文件系统中且压缩前大小为1GB的文件为例。如果HDFS的块大小设置为128MB,那么该文件将被存储在8个块中,把这个文件作为输入数据的MapReduce作业,将创建8个输入分片,其中每个分片作为一个单独的map任务的输入被独立处理。

    现在想象一下,文件是经过gzip压缩的,且压缩后文件大小为1GB。与以前一样,HDFS将这个文件保存为8个数据块。但是,将每个数据块单独作为一个输入分片是无法实现工作的,因为无法实现从gzip压缩数据流的任意位置读取数据,所以让map任务独立与其他任务进行数据读取是行不通的。gzip格式使用DEFLATE算法来存储压缩后的数据,而DEFLATE算法将数据存储在一系列连续的压缩块中。问题在于每个块的起始位置并没有以任何形式标记,所以读取时无法从数据流的任意当前位置前进到下一块的起始位置读取下一个数据块,从而实现与整个数据流的同步。由于上述原因,gzip并不支持文件切分。

    在这种情况下,MapReduce会采用正确的做法,它不会尝试切分gzip压缩文件,因为它知道输入的时gzip压缩文件(通过文件扩展名看出)且gzip不持支切分。这是可行的,但牺牲了数据的本地性:一个map任务处理8个HDFS块,而其中大多数块并没有存储在执行该map任务的节点上。而且,map任务数量越少,作业的粒度就越大,因而运行的时间可能会更长。

    在前面假设的例子中,如果文件时通过LZO压缩的,我们会面临相同的问题,因为这个压缩格式也不支持数据读取和数据流同步。但是,在预处理LZO文件的时候使用包含在Hadoop LZO库文件中的索引工具时可能的,你可以在Google和Github网站上获得该库。该工具构建了切分点索引,如果使用恰当的MapReduce输入格式可有效实现文件的可切分特性。

    另一方面,bzip2文件提供不同数据块之间的同步标识(pi的48位近似值),因而它支持切分。可以参考前面的表,了解每个压缩格式是否支持可切分

    该使用哪种压缩格式

    Hadoop应用处理的数据集非常大,因此需要借助与压缩。使用哪种压缩格式,与待处理的文件的大小、格式和所使用的工具有关。下面有一些建议,大致是按照效率从高到底排列的。

    • 使用容器文件格式,例如顺序文件(Sequence File)、Avro数据文件、ORCFile或者Parquet文件,所有这些文件格式同时支持压缩和切分。通常最好与一个快速压缩工具联合使用,例如,LZO、LZ4或者Snappy
    • 使用支持切分的压缩格式,例如bzip2(尽管bzip2非常慢),或者使用通过索引实现切分的压缩格式,例如LZO
    • 在应用中将文件切分成块,并使用任意一种压缩格式为每个数据块建立压缩文件(不论它是否支持切分)。这种情况下,需要合理选择数据块大小,以确保压缩后的数据块的大小近似于HDFS块的大小
    • 存储未经压缩的文件

    对于大文件来说,不要使用不支持切分整个文件的压缩格式,因为会失去数据的本地性,进而造成MapReduce应用效率低下

    后记

    本文内容参考Hadoop权威指南

    展开全文
  • qt 压缩解压缩文件和文件夹源码

    热门讨论 2011-10-18 11:39:32
    可是使用quazip类库实现文件和文件夹的压缩与解压缩,经测试可以使用
  • 哈夫曼实现文件压缩压缩(c语言)

    万次阅读 多人点赞 2019-01-23 17:04:47
    写一个对文件进行压缩和解压缩的程序,功能如下: ① 可以对纯英文文档实现压缩和解压; ② 较好的界面程序运行的说明。 介绍哈夫曼: 效率最高的判别树即为哈夫曼树 在计算机数据处理中,霍夫曼编码...

    写一个对文件进行压缩和解压缩的程序,功能如下:

    ① 可以对纯英文文档实现压缩和解压;

    ② 较好的界面程序运行的说明。

     

     

    介绍哈夫曼:

     

    效率最高的判别树即为哈夫曼树

    在计算机数据处理中,霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。

    例如,在英文中,e的出现机率最高,而z的出现概率则最低。当利用霍夫曼编码对一篇英文进行压缩时,e极有可能用一个比特来表示,而z则可能花去25个比特(不是26)。用普通的表示方法时,每个英文字母均占用一个字节,即8个比特。二者相比,e使用了一般编码的1/8的长度,z则使用了3倍多。倘若我们能实现对于英文中各个字母出现概率的较准确的估算,就可以大幅度提高无损压缩的比例。

    霍夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。树的路径长度是从树根到每一结点的路径长度之和,记为WPL=(W1*L1+W2*L2+W3*L3+...+Wn*Ln),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,...n)。可以证明霍夫曼树的WPL是最小的。

     

    文件压缩与解压

    姓名:  范天祚 

    1 程序说明

    1.1数据结构

    哈夫曼树

    1.2函数功能说明

    printfPercent界面

    compress()读取文件内容并加以压缩,将压缩内容写入另一个文档

    uncompress()解压缩文件,并将解压后的内容写入新文件

    1.3 程序编写的思路及流程

    压缩:统计字符出现次数、将节点按出现次数排序、构造哈夫曼树、设置字符编码、读文件字符、按设置好的编码替换字符、写入存储文件

    解压:读取文件各参数、转换成二进制码、按码求对应字符、写入存储文件

    #define _CRT_SECURE_NO_WARNINGS
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    struct head
    {
        int b;						  //字符
        long count;                   //文件中该字符出现的次数
        long parent, lch, rch;        //make a tree
        char bits[256];               //the huffuman code
    };
    
    struct head header[512], tmp;  //节点树
    
    void printfPercent(int per)
    {
    	int i = 0;
    	printf("|");
    	for(i = 0; i < 10; i++)
    	{
    		if(i < per/10)
    			printf(">");
    		else
    			printf("-");
    	}
    	printf("|已完成%d%%\n",per);
    }
    
    //函数:compress()
    //作用:读取文件内容并加以压缩
    //将压缩内容写入另一个文档
    int compress(const char *filename,const char *outputfile)
    {
        char buf[512];
        unsigned char c;
        long i, j, m, n, f;
        long min1, pt1, flength;
        FILE *ifp, *ofp;
    	int per = 10;
        ifp = fopen(filename, "rb");                  //打开原始文件
        if (ifp == NULL)
        {
            printf("打开文件失败:%s\n",filename);
            return 0;                             //如果打开失败,则输出错误信息
        }
        ofp = fopen(outputfile,"wb");                 //打开压缩后存储信息的文件
        if (ofp == NULL)
        {
            printf("打开文件失败:%s\n",outputfile);
            return 0;
        }
        flength = 0;
        while (!feof(ifp))
        {
            fread(&c, 1, 1, ifp);
            header[c].count ++;                       //读文件,统计字符出现次数
            flength ++;                               //记录文件的字符总数
        }
        flength --;
        header[c].count --;
        for (i = 0; i < 512; i ++)                    //HUFFMAN算法中初始节点的设置
        {
            if (header[i].count != 0)
                header[i].b = (unsigned char) i;
            else
                header[i].b = -1;
            header[i].parent = -1;
            header[i].lch = header[i].rch = -1;
        }
    
        for (i = 0; i < 256; i ++)                    //将节点按出现次数排序
        {
            for (j = i + 1; j < 256; j ++)
            {
                if (header[i].count < header[j].count)
                {
                    tmp = header[i];
                    header[i] = header[j];
                    header[j] = tmp;
                }
            }
        }
    
    
        for (i = 0; i < 256; i ++)                    //统计不同字符的数量
    	{
            if (header[i].count == 0)
                break;
    	}
    
        n = i;
        m = 2 * n - 1;
        for (i = n; i < m; i ++)
        {
            min1 = 999999999;
            for (j = 0; j < i; j ++)
            {
                if (header[j].parent != -1) continue;
                if (min1 > header[j].count)
                {
                    pt1 = j;
                    min1 = header[j].count;
                    continue;
                }
            }
            header[i].count = header[pt1].count;
            header[pt1].parent = i;
            header[i].lch = pt1;
            min1 = 999999999;
            for (j = 0; j < i; j ++)
            {
                if (header[j].parent != -1) continue;
                if (min1 > header[j].count)
                {
                    pt1 = j;
                    min1 = header[j].count;
                    continue;
                }
            }
            header[i].count += header[pt1].count;
            header[i].rch = pt1;
            header[pt1].parent = i;
        }
    
        for (i = 0; i < n; i ++)                        //构造HUFFMAN树,设置字符的编码
        {
            f = i;
            header[i].bits[0] = 0;
            while (header[f].parent != -1)
            {
                j = f;
                f = header[f].parent;
                if (header[f].lch == j)
                {
                    j = strlen(header[i].bits);
                    memmove(header[i].bits + 1, header[i].bits, j + 1);
                    header[i].bits[0] = '0';
                }
                else
                {
                    j = strlen(header[i].bits);
                    memmove(header[i].bits + 1, header[i].bits, j + 1);
                    header[i].bits[0] = '1';
                }
            }
        }
    
        //下面的就是读原文件的每一个字符,按照设置好的编码替换文件中的字符
        fseek(ifp, 0, SEEK_SET);                                                //将指针定在文件起始位置
        fseek(ofp, 8, SEEK_SET);                                //以8位二进制数为单位进行读取
        buf[0] = 0;
        f = 0;
        pt1 = 8;
    
    	printf("读取将要压缩的文件:%s\n",filename);
    	printf("当前文件有:%d字符\n",flength);
    	printf("正在压缩\n");
    
        while (!feof(ifp))
        {
            c = fgetc(ifp);
            f ++;
            for (i = 0; i < n; i ++)
            {
                if (c == header[i].b) break;
            }
            strcat(buf, header[i].bits);
            j = strlen(buf);
            c = 0;
            while (j >= 8)                                             //当剩余字符数量不小于8个时
            {
                for (i = 0; i < 8; i ++)                               //按照八位二进制数转化成十进制ASCII码写入文件一次进行压缩
                {
                    if (buf[i] == '1') c = (c << 1) | 1;
                    else c = c << 1;
                }
                fwrite(&c, 1, 1, ofp);
                pt1 ++;
                strcpy(buf, buf + 8);
                j = strlen(buf);
            }
    		if(100 * f/flength > per)
    		{
    			printfPercent(per);
    			per += 10;
    		}
            if (f == flength)
    			break;
        }
    	printfPercent(100);
    
        if (j > 0)                                                      //当剩余字符数量少于8个时
        {
            strcat(buf, "00000000");
            for (i = 0; i < 8; i ++)
            {
                if (buf[i] == '1') c = (c << 1) | 1;
                else c = c << 1;                                        //对不足的位数进行补零
            }
            fwrite(&c, 1, 1, ofp);
            pt1 ++;
        }
        fseek(ofp, 0, SEEK_SET);                                        //将编码信息写入存储文件
    	fwrite(&flength,1,sizeof(flength),ofp);
        fwrite(&pt1, sizeof(long), 1, ofp);
        fseek(ofp, pt1, SEEK_SET);
        fwrite(&n, sizeof(long), 1, ofp);
        for (i = 0; i < n; i ++)
        {
    		tmp = header[i];
    
            fwrite(&(header[i].b), 1, 1, ofp);
    		pt1++;
            c = strlen(header[i].bits);
            fwrite(&c, 1, 1, ofp);
    		pt1++;
            j = strlen(header[i].bits);
    
            if (j % 8 != 0)                                             //当位数不满8时,对该数进行补零操作
            {
                for (f = j % 8; f < 8; f ++)
                    strcat(header[i].bits, "0");
            }
    
            while (header[i].bits[0] != 0)
            {
                c = 0;
                for (j = 0; j < 8; j ++)
                {
                    if (header[i].bits[j] == '1') c = (c << 1) | 1;
                    else c = c << 1;
                }
                strcpy(header[i].bits, header[i].bits + 8);
                fwrite(&c, 1, 1, ofp);                                            //将所得的编码信息写入文件
    			pt1++;
            }
    
    		header[i] = tmp;
        }
        fclose(ifp);
        fclose(ofp);                                                              //关闭文件
    
    	printf("压缩后文件为:%s\n",outputfile);
        printf("压缩后文件有:%d字符\n",pt1 + 4);
    
        return 1;                                       //返回压缩成功信息
    }
    
    
    //函数:uncompress()
    //作用:解压缩文件,并将解压后的内容写入新文件
    int uncompress(const char *filename,const char *outputfile)
    {
        char buf[255], bx[255];
        unsigned char c;
    	char out_filename[512];
        long i, j, m, n, f, p, l;
        long flength;
    	int per = 10;
    	int len = 0;
        FILE *ifp, *ofp;
    	char c_name[512] = {0};
        ifp = fopen(filename, "rb");                                              //打开文件
        if (ifp == NULL)
        {
            return 0;     //若打开失败,则输出错误信息
        }
    
    													  //读取原文件长
    	if(outputfile)
    		strcpy(out_filename,outputfile);
    	else
    		strcpy(out_filename,c_name);
    
        ofp = fopen(out_filename, "wb");                                            //打开文件
        if (ofp == NULL)
        {
            return 0;
        }
    
    	fseek(ifp,0,SEEK_END);
    	len = ftell(ifp);
    	fseek(ifp,0,SEEK_SET);
    
    	printf("将要读取解压的文件:%s\n",filename);
    	printf("当前文件有:%d字符\n",len);
    	printf("正在解压\n");
    
        fread(&flength, sizeof(long), 1, ifp);                                    //读取原文件长
        fread(&f, sizeof(long), 1, ifp);
        fseek(ifp, f, SEEK_SET);
        fread(&n, sizeof(long), 1, ifp);                                          //读取原文件各参数
        for (i = 0; i < n; i ++)                                                  //读取压缩文件内容并转换成二进制码
        {
            fread(&header[i].b, 1, 1, ifp);
            fread(&c, 1, 1, ifp);
            p = (long) c;
            header[i].count = p;
            header[i].bits[0] = 0;
            if (p % 8 > 0) m = p / 8 + 1;
            else m = p / 8;
            for (j = 0; j < m; j ++)
            {
                fread(&c, 1 , 1 , ifp);
                f = c;
                _itoa(f, buf, 2);
                f = strlen(buf);
                for (l = 8; l > f; l --)
                {
                    strcat(header[i].bits, "0");                                  //位数不足,执行补零操作
                }
                strcat(header[i].bits, buf);
            }
            header[i].bits[p] = 0;
        }
    
        for (i = 0; i < n; i ++)
        {
            for (j = i + 1; j < n; j ++)
            {
                if (strlen(header[i].bits) > strlen(header[j].bits))
                {
                    tmp = header[i];
                    header[i] = header[j];
                    header[j] = tmp;
                }
            }
        }
    
        p = strlen(header[n-1].bits);
        fseek(ifp, 8, SEEK_SET);
        m = 0;
        bx[0] = 0;
    
    
        while (1)
        {
            while (strlen(bx) < (unsigned int)p)
            {
                fread(&c, 1, 1, ifp);
                f = c;
                _itoa(f, buf, 2);
                f = strlen(buf);
                for (l = 8; l > f; l --)
                {
                    strcat(bx, "0");
                }
                strcat(bx, buf);
            }
            for (i = 0; i < n; i ++)
            {
                if (memcmp(header[i].bits, bx, header[i].count) == 0) break;
            }
            strcpy(bx, bx + header[i].count);
            c = header[i].b;
            fwrite(&c, 1, 1, ofp);
            m ++;
    
    		if(100 *  m/flength > per)
    		{
    			printfPercent(per);
    			per += 10;
    		}
            if (m == flength) break;
        }
    	printfPercent(100);
    
        fclose(ifp);
        fclose(ofp);
    
    	printf("解压后文件为:%s\n",out_filename);
        printf("解压后文件有:%d字符\n",flength);
    
        return 1;                   //输出成功信息
    }
    
    int main(int argc,const char *argv[])
    {
    	memset(&header,0,sizeof(header));
        memset(&tmp,0,sizeof(tmp));
    
    	compress("测试文档.txt","测试文档.txt.zip");
    	uncompress("测试文档.txt.zip","测试文档.txt 解压后.txt");
    	system("pause");
    
    	return 0;
    }
    

     

    2 功能展示

    2.1 控制台显示

    2.2 文件效果

    开始时只有一个文件《测试文档.txt》:

    打开《测试文档.txt》

    《测试文档.txt》文件大小:

    程序运行结束后多了两个文件:

    以文本形式打开压缩二进制文件《测试文档.txt.zip》:

    《测试文档.txt.zip》文件属性:

    展开全文
  • Java实现zip文件压缩与解压缩--附完整代码

    万次阅读 多人点赞 2019-07-26 14:54:09
    Java实现文件压缩与解压缩-----zip、.7z1. 基本概念1.1 Java中实现zip的压缩与解压缩1.1.1 基本概念1.1.2 zip压缩代码实现1.3 zip压缩代码改进 1. 基本概念 1.1 Java中实现zip的压缩与解压缩 1.1.1 基本概念 ...

    Java中实现zip的压缩与解压缩

    1 基本概念

    1. ZipOutputStream 实现文件的压缩
    • ZipOutputStream (OutputStream out) 创建新的zip输出流
    • void putNextEntry(ZipEntry e) 开始写入新的zip文件条目并将流定位到条目数据的开始处
      条目指的是一个文件夹下的多个文件。
    • ZipEntry(String name) 使用指定名称创建新的zip条目
    1. ZipIutputStream实现文件的解压
    • ZipIutputStream (IutputStream out) 创建新的zip输入流
    • ZipEntry getNextEntry()读取下一个zip条目并将流定位到该条目数据的开始处

    2 zip压缩

    2.1 zip压缩代码实现

    1. 程序完整代码
    import java.io.BufferedInputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipOutputStream;
    
    public class Uzip {
        /**
         * 压缩
         */
    
        public static void zip(String input, String output, String name) throws Exception {
        //要生成的压缩文件
            ZipOutputStream out = new ZipOutputStream(new FileOutputStream(output));
            String[] paths = input.split("\\|");
            File[] files = new File[paths.length];
            byte[] buffer = new byte[1024];
            for (int i = 0; i < paths.length; i++) {
                files[i] = new File(paths[i]);
            }
            for (int i = 0; i < files.length; i++) {
                FileInputStream fis = new FileInputStream(files[i]);
                if (files.length == 1 && name != null) {
                    out.putNextEntry(new ZipEntry(name));
                } else {
                    out.putNextEntry(new ZipEntry(files[i].getName()));
                }
                int len;
                // 读入需要下载的文件的内容,打包到zip文件
                while ((len = fis.read(buffer)) > 0) {
                    out.write(buffer, 0, len);
                }
                out.closeEntry();
                fis.close();
            }
            out.close();
        }
    
        public static void main(String[ ] args){
            try {
                zip("E:\\Testzip\\test\\ytt.html","E:\\Test.zip","testytt");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    

    maven依赖:

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-compress</artifactId>
            <version>1.12</version>
        </dependency>
    
    1. 运行程序:

    在这里插入图片描述

    3.程序解读:

    • ZipEntry(String name) 只能实现一个固定条目的压缩,也就是你压缩什么就得写仔细它的路径,多个子文件压缩写法如下:
    public static void main(String[ ] args){
            try {
                zip("E:\\Testzip\\test\\ytt.html|E:\\Testzip\\uugg.html","E:\\Test.zip",null);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    注意

    一定要写全E:\\Testzip\\test\\ytt.html,如果写E:\\Testzip\\test\\ytt会报错,报错信息无访问权限。

    String input :定义的是待压缩文件的条目。

    String output:定义得到的压缩文件包.zip的名字。

    String name:定义压缩后的条目的名字,如果与压缩前保持一致,定义name为null即可。

    此程序无法实现对空文件夹的压缩。

    2.2 zip压缩代码改进

    改进的代码可以实现对任意文件的压缩,注意要写全文件类型,比如ytt.html,不允许省略.html。

    import java.io.*;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipOutputStream;
    
    /**
     * date: 2019/07/26
     * writed by yangtingting
     */
    public class FileZip {
        /**
         * zip文件压缩
         * @param inputFile 待压缩文件夹/文件名
         * @param outputFile 生成的压缩包名字
         */
    
        public static void ZipCompress(String inputFile, String outputFile) throws Exception {
            //创建zip输出流
            ZipOutputStream out = new ZipOutputStream(new FileOutputStream(outputFile));
            //创建缓冲输出流
            BufferedOutputStream bos = new BufferedOutputStream(out);
            File input = new File(inputFile);
            compress(out, bos, input,null);
            bos.close();
            out.close();
        }
        /**
         * @param name 压缩文件名,可以写为null保持默认
         */
        //递归压缩
        public static void compress(ZipOutputStream out, BufferedOutputStream bos, File input, String name) throws IOException {
            if (name == null) {
                name = input.getName();
            }
            //如果路径为目录(文件夹)
            if (input.isDirectory()) {
                //取出文件夹中的文件(或子文件夹)
                File[] flist = input.listFiles();
    
                if (flist.length == 0)//如果文件夹为空,则只需在目的地zip文件中写入一个目录进入
                {
                    out.putNextEntry(new ZipEntry(name + "/"));
                } else//如果文件夹不为空,则递归调用compress,文件夹中的每一个文件(或文件夹)进行压缩
                {
                    for (int i = 0; i < flist.length; i++) {
                        compress(out, bos, flist[i], name + "/" + flist[i].getName());
                    }
                }
            } else//如果不是目录(文件夹),即为文件,则先写入目录进入点,之后将文件写入zip文件中
            {
                out.putNextEntry(new ZipEntry(name));
                FileInputStream fos = new FileInputStream(input);
                BufferedInputStream bis = new BufferedInputStream(fos);
                int len;
                //将源文件写入到zip文件中
                byte[] buf = new byte[1024];
                while ((len = bis.read(buf)) != -1) {
                    bos.write(buf,0,len);
                }
                bis.close();
                fos.close();
            }
        }
        public static void main(String[] args) {
            try {
                ZipCompress("D:\\Test", "D:\\TestbyYTT.zip");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    
    

    3 zip解压

    3.1 zip不完美实现

    1. 这个网上有很多代码的,比如:
    package file;
     
    import java.io.*;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipFile;
    import java.util.zip.ZipInputStream;
     
    public class Decompressing {
        public static void main(String[] args) {
            File file = new File("E:\\hello.zip");//当前压缩文件
            ZipInputStream zin;//创建ZipInputStream对象
            try {
                ZipFile zipFile = new ZipFile(file);//创建压缩文件对象
                zin = new ZipInputStream(new FileInputStream(file));//实例化对象,指明要解压的文件
                ZipEntry entry ;
                while (((entry=zin.getNextEntry())!=null)&& !entry.isDirectory()){//如果entry不为空,并不在同一个目录下
                    File tmp = new File(entry.getName());//解压出的文件路径
                    if (!tmp.exists()){//如果文件不存在
                        tmp.getParentFile().mkdirs();//创建文件父类文件夹路径
                        OutputStream os = new FileOutputStream(tmp);//将文件目录中的文件放入输出流
                        //用输入流读取压缩文件中制定目录中的文件
                        InputStream in = zipFile.getInputStream(entry);
                        int count = 0;
                        while ((count = in.read())!=-1){//如有输入流可以读取到数值
                            os.write(count);//输出流写入
                        }
                        os.close();
                        in.close();
                    }
                    zin.closeEntry();
                    System.out.println(entry.getName()+"解压成功");
                }
                zin.close();
     
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
     
    }
    

    以上代码运行时会抛出异常!!!!!!!!!

    做出相应更改:

    import java.io.*;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipInputStream;
    
    /**
     * date:2019/7/26
     * writed by yangtingting
     */
    
    public class zipUncompress {
        /**
         * zip文件解压
         *
         * @param inputFile   待解压文件夹/文件
         * @param destDirPath 解压路径
         */
        public static void zipUncompress(String inputFile, String destDirPath) throws Exception {
            File srcFile = new File(inputFile);//获取当前压缩文件
            // 判断源文件是否存在
            if (!srcFile.exists()) {
                throw new Exception(srcFile.getPath() + "所指文件不存在");
            }
            //开始解压
            //构建解压输入流
            ZipInputStream zIn = new ZipInputStream(new FileInputStream(srcFile));
            ZipEntry entry = null;
            File file = null;
            while ((entry = zIn.getNextEntry()) != null) {
                if (!entry.isDirectory()) {
                    file = new File(destDirPath, entry.getName());
                    if (!file.exists()) {
                        new File(file.getParent()).mkdirs();//创建此文件的上级目录
                    }
                    OutputStream out = new FileOutputStream(file);
                    BufferedOutputStream bos = new BufferedOutputStream(out);
                    int len = -1;
                    byte[] buf = new byte[1024];
                    while ((len = zIn.read(buf)) != -1) {
                        bos.write(buf, 0, len);
                    }
                    // 关流顺序,先打开的后关闭
                    bos.close();
                    out.close();
                }
            }
        }
        public static void main(String[] args) {
            try {
                zipUncompress("D:\\ytt.zip", "D:\\ytt的解压文件\\");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    完美运行!!!

    3.2 zip解压完美实现

    改进后的代码

    import java.io.*;
    import java.util.Enumeration;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipFile;
    
    /**
     * date:2019/7/26
     * writed by yangtingting
     */
    
    public class zipUncompress {
        /**
         * zip文件解压
         * @param inputFile  待解压文件夹/文件
         * @param destDirPath  解压路径
         */
        public static void zipUncompress(String inputFile,String destDirPath) throws Exception {
            File srcFile = new File(inputFile);//获取当前压缩文件
            // 判断源文件是否存在
            if (!srcFile.exists()) {
                throw new Exception(srcFile.getPath() + "所指文件不存在");
            }
            ZipFile zipFile = new ZipFile(srcFile);//创建压缩文件对象
            //开始解压
            Enumeration<?> entries = zipFile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = (ZipEntry) entries.nextElement();
                // 如果是文件夹,就创建个文件夹
                if (entry.isDirectory()) {
                    String dirPath = destDirPath + "/" + entry.getName();
                    srcFile.mkdirs();
                } else {
                    // 如果是文件,就先创建一个文件,然后用io流把内容copy过去
                    File targetFile = new File(destDirPath + "/" + entry.getName());
                    // 保证这个文件的父文件夹必须要存在
                    if (!targetFile.getParentFile().exists()) {
                        targetFile.getParentFile().mkdirs();
                    }
                    targetFile.createNewFile();
                    // 将压缩文件内容写入到这个文件中
                    InputStream is = zipFile.getInputStream(entry);
                    FileOutputStream fos = new FileOutputStream(targetFile);
                    int len;
                    byte[] buf = new byte[1024];
                    while ((len = is.read(buf)) != -1) {
                        fos.write(buf, 0, len);
                    }
                    // 关流顺序,先打开的后关闭
                    fos.close();
                    is.close();
                }
            }
        }
        public static void main(String[] args) {
            try {
                zipUncompress("D:\\ytt.zip","D:\\ytt的解压文件");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    

    4 FileZip.class 实现.zip压缩与解压

    完整代码:

    import java.io.*;
    import java.util.zip.*;
    import java.util.zip.ZipEntry;
    
    /**
     * date: 2019/07/26
     * writed by yangtingting
     */
    public class FileZip {
        /**
         * zip文件压缩
         * @param inputFile 待压缩文件夹/文件名
         * @param outputFile 生成的压缩包名字
         */
    
        public static void ZipCompress(String inputFile, String outputFile) throws Exception {
            //创建zip输出流
            ZipOutputStream out = new ZipOutputStream(new FileOutputStream(outputFile));
            //创建缓冲输出流
            BufferedOutputStream bos = new BufferedOutputStream(out);
            File input = new File(inputFile);
            compress(out, bos, input,null);
            bos.close();
            out.close();
        }
        /**
         * @param name 压缩文件名,可以写为null保持默认
         */
        //递归压缩
        public static void compress(ZipOutputStream out, BufferedOutputStream bos, File input, String name) throws IOException {
            if (name == null) {
                name = input.getName();
            }
            //如果路径为目录(文件夹)
            if (input.isDirectory()) {
                //取出文件夹中的文件(或子文件夹)
                File[] flist = input.listFiles();
    
                if (flist.length == 0)//如果文件夹为空,则只需在目的地zip文件中写入一个目录进入
                {
                    out.putNextEntry(new ZipEntry(name + "/"));
                } else//如果文件夹不为空,则递归调用compress,文件夹中的每一个文件(或文件夹)进行压缩
                {
                    for (int i = 0; i < flist.length; i++) {
                        compress(out, bos, flist[i], name + "/" + flist[i].getName());
                    }
                }
            } else//如果不是目录(文件夹),即为文件,则先写入目录进入点,之后将文件写入zip文件中
            {
                out.putNextEntry(new ZipEntry(name));
                FileInputStream fos = new FileInputStream(input);
                BufferedInputStream bis = new BufferedInputStream(fos);
                int len=-1;
                //将源文件写入到zip文件中
                byte[] buf = new byte[1024];
                while ((len = bis.read(buf)) != -1) {
                    bos.write(buf,0,len);
                }
                bis.close();
                fos.close();
            }
        }
    
        /**
         * zip解压
         * @param inputFile 待解压文件名
         * @param destDirPath  解压路径
         */
    
        public static void ZipUncompress(String inputFile,String destDirPath) throws Exception {
            File srcFile = new File(inputFile);//获取当前压缩文件
            // 判断源文件是否存在
            if (!srcFile.exists()) {
                throw new Exception(srcFile.getPath() + "所指文件不存在");
            }
            //开始解压
            //构建解压输入流
            ZipInputStream zIn = new ZipInputStream(new FileInputStream(srcFile));
            ZipEntry entry = null;
            File file = null;
            while ((entry = zIn.getNextEntry()) != null) {
                if (!entry.isDirectory()) {
                    file = new File(destDirPath, entry.getName());
                    if (!file.exists()) {
                        new File(file.getParent()).mkdirs();//创建此文件的上级目录
                    }
                    OutputStream out = new FileOutputStream(file);
                    BufferedOutputStream bos = new BufferedOutputStream(out);
                    int len = -1;
                    byte[] buf = new byte[1024];
                    while ((len = zIn.read(buf)) != -1) {
                        bos.write(buf, 0, len);
                    }
                    // 关流顺序,先打开的后关闭
                    bos.close();
                    out.close();
                }
            }
        }
    
    
        public static void main(String[] args) {
            try {
                ZipCompress("D:\\Test", "D:\\TestbyYTT.zip");
                ZipUncompress("D:\\ytt.zip","D:\\ytt的解压文件");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    展开全文
  • 针对WINZIP压缩文件,黑客最常使用的工具就是Elcomsoft公司的“Advanced ZIP Password Recovery”(简称AZPR),AZPR提供了一个图形化的用户界面,黑客经过几个简单的步骤就可以破解ZIP压缩文件包的密...
  • js压缩文件还原

    千次阅读 2019-03-12 17:33:26
    js压缩文件还原网址:https://beautifier.io/
  • Java多文件压缩下载解决方案

    万次阅读 2018-09-07 18:00:33
    会员运营平台经过改版后页面增加了许多全部下载链接,上周上线比较仓促,全部下载是一个直接下载ZIP压缩文件的链接,每个ZIP压缩文件都是由公司运营人员将页面需要下载的文件全部压缩成一个ZIP压缩文件,然后通过...
  • 网上找到的问题原因是:ZipOutputStream的流没有关闭 其实说的也没错,但是我们都知道现在的java是可以自动关闭的,如下: try( ZipOutputStream zos = new ZipOutputStream(fileOutputStream) ...
  • 在iOS使用ZipArchive压缩和解压缩文件

    热门讨论 2014-01-28 12:40:35
    一个在iOS使用ZipArchive实现压缩和解压缩文件的demo
  • zlib的C++封装类压缩文件

    热门讨论 2014-11-19 22:50:47
    项目培训需要封装了zlib库生成压缩文件,没什么技术含量,放出来.^-^. 需要的童鞋拿去,还是想提醒下偷懒的童鞋要看懂代码。 http://blog.csdn.net/infoworld/article/details/41290969
  • HDFS文件压缩

    千次阅读 2018-01-26 13:52:53
    减少储存文件所需空间,还可以降低其在网络上传输的时间。 hadoop下各种压缩算法的压缩压缩算法 原始文件大小 压缩后的文件大小 压缩速度 解压速度 gzip 8.3GB 1.8GB 17.5MB/s 58MB/s bzip2 8.3GB 1.1GB 2.4MB/s
  • tar压缩文件

    千次阅读 2020-12-20 10:31:07
    使用tar压缩文件 tar -zcvf test.tar.gz ./test/ 该命令表示压缩当前文件夹下的文件夹test,压缩后缀名为test.tar.gz 如果不需要压缩成gz,只需要后缀为tar格式的,那么输入如下命令: tar -cvf test.tar ./test/ ...
  • 一下代码实现的是将多个... * 压缩文件 * @param exportFilePathList * @param response */ public static void downloadPictureZip(List&lt;String&gt; exportFilePathList, HttpServletResponse ...
  • Gzip压缩软件(gz文件压缩)

    热门讨论 2009-05-09 13:16:33
    很强大的软件,Gzip压缩软件(gz文件压缩) 武汉中原科技提供
  • java多文件压缩成zip文件并实现下载

    热门讨论 2012-10-25 19:40:27
    本例是把多个文件压缩成一个zip文件,然后下载到本地。 项目完整,下载解压即可运行。
  • java 实现压缩单个文件

    千次阅读 2018-07-29 22:13:59
    先来实现一个简单的单文件压缩,主要是为了解一下压缩需要使用到的流。。 效果: 说明:压缩实现使用ZipOutputStream 代码: package com.gx.compress; import java.io.BufferedInputStream; import java....
  • 前言 最近遇到一个问题,同事微信发我一个后缀为zip的压缩文件,但点击下载后无法打开。 而苹果手机也不像一些安卓手机自带压缩解压工具,那应该怎么办呢?方法一:使用第三方软件-文件全能王首先在微信上下载后点击...
  • 我本来是在eclipse中配置tomcat8.5版本开始,...当然这种方法可以适用在压缩软件里面修改压缩完成后的文件 在这里我使用的软件是WinRAR压缩软件 1、选择要执行修改的文件 2、点击右键,进行修改 3、找出在...
  • C++ zip解压缩压缩

    2018-03-30 17:08:27
    只需要包含头文件,就可以进行文件压缩和解压缩。可查看readme 或者文件中demo 简单易用。
  •  可能是复制的不完全,压缩文件真的坏了,但也可能是另一种情况,如在win10的系统中把压缩文件复制到了win7的系统中,因为winrar版本差异太大导致无法解压,这时候只需要到网上下载WinRar最新版到本地,再解...
  • java多个文件压缩成zip文件

    千次阅读 2018-08-15 14:14:05
     文件压缩 */ private static void ZipFiles(java.io.File[] srcfile, java.io.File zipfile) { byte[] buf=new byte[1024]; ZipOutputStream out=null; try { out=new ZipOutputStream(new Fi...
  • Python 创建加密压缩文件

    千次阅读 2019-11-05 00:14:16
    Python 创建加密压缩文件 1. 配置ZLib http://www.winimage.com/zLibDll/minizip.html 下载之后将 zlibwapi.dll 放在 demo/目录下,与 minizip.exe 一起使用 2. 命令行使用 minizip.exe >minizip.exe ...
  • java压缩文件到指定位置

    千次阅读 2019-12-06 15:03:56
    //构建压缩文件File File zipFile = new File(zipFileName); //初期化ZIP流 ZipOutputStream out = null; try { //构建ZIP流对象 out = new ZipOutputStream(new FileOutputStream(zipFile)); //循环处理传...
  • Qt5压缩解压缩文件/

    千次阅读 2019-08-02 15:21:15
    全网居然没找到一个直接压缩整个文件夹的c/c++库,就算找到了zlib,karchive,archive等开源库,基本也是在Linux下支持的较好,在win...由此思路,如果自己要压缩文件夹,那就自己遍历,然后自己定义压缩文件的格式...
  • 在右键增加操作菜单 把每个文件放到单独的压缩文件
  • 前端性能优化 压缩静态文件gzip

    千次阅读 2018-10-16 10:41:09
    前端压缩的方式很多,依赖java的ant工具,前端自己打包压缩的grunt,gulp,webpack,这些压缩也很重要,基本上压缩50%以上,下面我们对压缩文件来个对比。 如图所示,这是未压缩的         ...
  • qt在linux系统下实现压缩文件

    千次阅读 2019-01-15 15:10:18
    编程进行文件压缩一般三种方法: 一是使用zlib的C++开源源码; 二是使用Quazip动态库; 三就是在子进程中使用第三方软件。 由于在windows中开发qt程序使用了zlib源码库;而linux下的命令行操作又如此简单。这里用...
  • Zlib文件压缩和解压

    千次阅读 2018-08-18 15:54:29
    打开欲压缩文件时文件的指针  FILE *  fp1;  // 创建压缩文件时的指针   errno_t err;  // 错误变量的定义 #ifdef WINDOWS_PLATFORM  err  =  fopen_s( & ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,027,570
精华内容 411,028
关键字:

压缩文件里能有压缩文件吗