一,项目需求
今天在工作中遇到这样一个问题,系统中,文件上传的过程中需要进行加密,下载时再进行对应的解密。当文件不是太大时没有问题,但是当文件过大时,比如有个几百M或者几个G时就出问题了。这么大文件进行一次性加密解密,时间太长以至于出现系统超时,甚至出现内存溢出的情况。如何解决?经过研究,我们在进行文件加密时其实没有必要加密整个文件,只加密其中的一部分(比如加密1M文件),然后和另外一部分不加密的文件拼接在一起,生成最终的加密文件。虽然只加密了其中一部分,但是因为加密其中的一部分,对整个文件的格式其实是有破坏的,所以最终生成的文件其实也是一个加密的状态,用户用对应的软件是打不开的,这样就起到了文件加密的作用。下面讲解实现方法和过程。
二,实现思路
我们假定只加密文件的前1M,将这个文件分为前1M和剩余文件两个部分,前1M加密,后面剩余的部分不加密,如图1:
图2即为加密后的文件结构,下面详述加密解密的步骤
- 第一步。我们取出文件前1M并进行加密,经测试,加密后的文件会增大一点,但不会太多,基本是几十个字节。
- 第二步。将前1M加密后的文件和分隔符进行拼接,这里分隔符的作用是在解密时依据分隔符找到加密的文件内容。
- 第三步。增加无用的填充的数据和第二步生成的文件拼接,保证大小为2M。这里填充到2M的目的也是为了解密时的方便,即解密时我们固定取加密文件的前2M来进行解密。
- 第四步。将第三步生成的2M文件和后面剩余未加密的文件进行拼接,生成最终的密文。至此加密工作完成。底下介绍解密过程。
- 第五步。解密,我们固定取密文的前2M文件,然后依据分隔符找到前1M的加密文件,然后进行解密,然后和剩余未加密的文件进行拼接,最终生成解密后的完成文件。
三,源码分析
主要的核心代码即为加密和解密的方法,源码如下:
private static String SEPARATOR = "******file_encode_separator******";//文件分段加密分隔符
private static int EN_LIMIT_SIZE = 1024 * 1024;//加密文件临界大小,1M.
private static int DE_LIMIT_SIZE = 2 * 1024 * 1024;//解密文件临界大小,2M.
/**
* 加密文件
*
* @return
* @throws Exception
*/
public static byte[] encryptFile(byte[] data) throws Exception {
if (data.length > EN_LIMIT_SIZE) {//大于1M
/**
* 取前1M并且加密
*/
byte[] data_1m = Arrays.copyOf(data, EN_LIMIT_SIZE);
byte[] data_1m_en = SM2Util.encrypt(SM2Util.PUBLIC_KEY, data_1m);
/**
* 读取剩余的文件
*/
byte[] data_left = Arrays.copyOfRange(data, EN_LIMIT_SIZE, data.length);
/**
* 合并加密的1M文件和分隔符
*/
byte[] data_1m_en_separator = ArrayUtils.byteMerger(data_1m_en, SEPARATOR.getBytes());
/**
* 填充至2M
*/
byte[] data_padding = new byte[DE_LIMIT_SIZE - data_1m_en_separator.length];
Arrays.fill(data_padding, (byte) 1);
byte[] data_2m = ArrayUtils.byteMerger(data_1m_en_separator, data_padding);
/**
*生成最终加密文件
*/
return ArrayUtils.byteMerger(data_2m, data_left);
} else {//小于等于1M,全部加密
return SM2Util.encrypt(SM2Util.PUBLIC_KEY, data);
}
}
/**
* 解密文件
*
* @return
*/
public static byte[] decryptFile(byte[] data) throws Exception {
if (data.length > DE_LIMIT_SIZE) {//大于2M
/**
* 取前2M并且解密
*/
byte[] data_2m = Arrays.copyOf(data, DE_LIMIT_SIZE);
int index_separator = ArrayUtils.searchInByteArray(data_2m, SEPARATOR.getBytes());
if (index_separator > 0) {//存在分隔符
byte[] data_pre_en = Arrays.copyOf(data_2m, index_separator);
byte[] data_pre = SM2Util.decrypt(SM2Util.PRIVATE_KEY, data_pre_en);
byte[] data_left = Arrays.copyOfRange(data, DE_LIMIT_SIZE, data.length);//取2M后的剩余部分文件
return ArrayUtils.byteMerger(data_pre, data_left);//生成最终解密文件
} else {
return SM2Util.decrypt(SM2Util.PRIVATE_KEY, data);
}
} else {//文件小于2M,采用全部解密
return SM2Util.decrypt(SM2Util.PRIVATE_KEY, data);
}
}
编写测试main方法进行测试:
String resource = "E:\\source.doc";
String file_en = "E:\\source_en.doc";
String file_de = "E:\\source_de.doc";
writeFile(file_en, encryptFile(readFile(resource)));
writeFile(file_de, decryptFile(readFile(file_en)));
四,最后总结
好了,至此项目中碰到大文件加密解密的处理技巧介绍完毕。今天分享给大家,希望对大家有用,欢迎留言,一起讨论。关注以下公众号,回复 “文件” 即可获得完整源代码。