ZipOutputStream:
- 使用
ZipOutputStream
创建了一个 ZIP 归档文件,这在需要将多个文件打包成一个文件时非常有用。 - 同样通过流式处理读取和写入数据,避免了内存溢出的问题。
GZIPOutputStream :
- 使用
GZIPOutputStream
直接对文件内容进行 GZIP 压缩,这是一种高效的压缩方式,适用于大多数需要减少数据大小以节省存储或传输时间的场景。 - 通过流式处理读取和写入数据,避免了将整个文件内容一次性加载到内存中,减少了内存使用的风险。
在实际开发中 GZIPOutputStream就已经足够用了,如果你的目的仅仅是压缩单个文件而不是创建 ZIP 归档,那么使用 ZipOutputStream
可能比 GZIPOutputStream
更复杂且效率稍低。
在实际开发中根据我们的业务需求不同我们的选择也不同
- 如果只是压缩单个文件:
GZIPOutputStream
更优,因为它直接使用GZIPOutputStream
进行压缩,这是为单个文件压缩设计的,且效率更高。 - 如果是压缩多个文件:ZipOutputStream更优,但相应的也比
GZIPOutputStream存在更多的编程困难
附上相应代码:
//ZipOutputStream
@PostMapping
public Result upload(@RequestParam("file") MultipartFile file) {
// 创建一个 ByteArrayOutputStream 来捕获 ZIP 输出的字节
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try (
// 创建一个 ZipOutputStream,用于将文件写入 ZIP 归档
// 这里,将其包装在 try-with-resources 语句中,以便自动关闭
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)
) {
// 创建一个 ZipEntry,它代表 ZIP 归档中的一个条目(即当前的file 文件)
ZipEntry zipEntry = new ZipEntry(file.getOriginalFilename());
// 将 ZipEntry 添加到 ZIP 归档中
zipOutputStream.putNextEntry(zipEntry);
try (BufferedInputStream bis = new BufferedInputStream(file.getInputStream())) {
// 创建一个缓冲区,用于读取和写入文件数据
byte[] buffer = new byte[1024];
int length;
// 从 MultipartFile 的 InputStream 读取数据,并将其写入 ZIP 归档
while ((length = bis.read(buffer)) > 0) {
zipOutputStream.write(buffer, 0, length);
}
}
// 关闭当前 ZIP 归档条目(即文件)
zipOutputStream.closeEntry();
} catch (IOException e) {
// 如果在 ZIP 处理过程中发生异常,则返回失败信息
return Result.fail("上传失败: " + e.getMessage());
}
// 获取 ZIP 归档的字节数据
byte[] zipBytes = byteArrayOutputStream.toByteArray();
// 将 ZIP 归档的字节数据插入到数据库中,数据库选择根据实际情况来定
// 注意:通常,会将 ZIP 文件保存时带有 .zip 扩展名但这里我只是压缩一个文件
// 我选择直接保存原文件类型,可以从数据库中获取字节数组时,直接转化为对应的格式,但是这种方法只适用于压缩一个文件,或文件类型是相同时使用
fileService.insert(zipBytes, file.getOriginalFilename());
// 返回上传成功的消息
return Result.success("上传成功");
}
//GZIPOutputStream
@PostMapping
public Result upload(@RequestParam("file") MultipartFile file) {
// 尝试资源自动关闭的代码块,包括 ByteArrayOutputStream、GZIPOutputStream 和 BufferedInputStream
try (
// 创建一个 ByteArrayOutputStream 来捕获 GZIP 输出的字节
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 创建一个 GZIPOutputStream,用于将文件内容压缩后写入 ByteArrayOutputStream
GZIPOutputStream gzipOut = new GZIPOutputStream(baos);
// 创建一个 BufferedInputStream 来包装 MultipartFile 的 InputStream,以提高读取效率
BufferedInputStream bis = new BufferedInputStream(file.getInputStream())
) {
// 创建一个缓冲区,用于从 BufferedInputStream 读取数据并写入 GZIPOutputStream
byte[] buffer = new byte[1024];
int len; // 用于存储每次从 BufferedInputStream 读取的字节数
// 循环读取 BufferedInputStream 中的数据,直到没有更多数据可读
while ((len = bis.read(buffer)) > 0) {
// 将缓冲区中的数据写入 GZIPOutputStream 进行压缩
gzipOut.write(buffer, 0, len);
}
// 完成 GZIP 压缩过程,确保所有待压缩的数据都被处理
gzipOut.finish();
// 从 ByteArrayOutputStream 获取压缩后的字节数据
byte[] zip = baos.toByteArray();
// 将压缩后的字节数据和原始文件名(或带有压缩后缀的新文件名)保存到数据库中
// 注意:通常,会在压缩文件添加一个特定的扩展名,如 ".gz",这里只是一个文件
fileService.insert(zip, file.getOriginalFilename());
// 返回上传成功的消息
return Result.success("上传成功");
} catch (IOException e) {
// 如果在文件读取、压缩或写入过程中发生 IOException,则返回上传失败的消息
return Result.fail("上传失败");
}
}
附上我的数据库设计:
类型 | 字段名 |
int | id |
longblob | zip |
varchar(255) | fileName |
这里之所以选择longblob来保存文件的字节数组,是因为在实际开发中所需压缩的文件大小都比较大而blob最大只能保存64KB的文件,但longblob最大可以保存4GB内存的文件。
这里附上blob相关类型的使用:
- Blob:
- 用于存储二进制大对象。
- 长度根据存储引擎的不同而有所变化,但通常能够存储的数据量远超过标准文本类型(如VARCHAR或TEXT)。
- 在MySQL中,Blob类型的最大长度可能会受到表行大小和存储引擎的限制。
- 最大可存储64KB,即65,535字节。
- TinyBlob:
- 类似于Blob,但用于存储较小的二进制数据。
- 最大长度通常比Blob小,适用于不需要存储大量二进制数据的场景。
- 最大可存储255个字节
- MediumBlob:
- 介于Blob和LongBlob之间,提供了适中的存储能力。
- 适用于存储中等大小的二进制数据,如中等大小的图片或音频文件。
- 最大可存储16MB,即16,777,215字节
- LongBlob:
- 用于存储非常大的二进制数据。
- 在MySQL中,LongBlob能够存储的最大数据量可以达到4GB(在MySQL 5.0.3及以后版本中),这对于存储大型文件如高清视频、大型图像集等非常有用。
使用LongBlob会遇到一个错误PacketTooBigException,这个问题的解决方法,这里我只说明MySql的解决方法,其他数据库遇到可以去查询解决办法
解决办法如下:
修改MySql的my.ini文件,通常在MySQL目录下,先剪切掉my.ini去到另一个目录因为MySQL是默认管理员修改的,直接修改my.ini是不可行的,在MySQL目录下新建一个my.ini,并去复制之前的my.ini粘贴到新的my.ini,在新的my.ini添加如下内容
[mysqld]
max_allowed_packet=1073741824
这里的1073741824是1GB,导致这个错误的原因就是因为MySql拒绝访问过大的文件,所以抛出这个错误,修改了这个之后就可以正常运行程序了