This repository has been archived by the owner on Jan 27, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
CompressUtils.java
281 lines (244 loc) · 10.1 KB
/
CompressUtils.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
package org.xueyao.utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
* 对文件、输出流提供压缩、解压操作的工具类
*
* @author xuan
* @version $Revision: 1.0 $, $Date: 2012-11-22 上午9:51:01 $
*/
public abstract class CompressUtils {
/**
* 压缩处理类,客户端通过实现此方法来将数据写入输出流。
*/
interface CompressHandler {
/**
* 对输出流进行处理的方法,可以将需要进行压缩的数据写入该输出流中。
* <p>
* {@link OutputStream#close()} 方法可以不调用,因为在工具方法中会进行关闭操作。
*
* @param out 输出流
* @throws IOException 发生 I/O 错误时应当抛出此异常
*/
void handle(OutputStream out) throws IOException;
}
private static final int BUFFER_SIZE = 8192;
/**
* 以 GZip 方式对输出流进行压缩后保存到文件。<strong>注意:如果目标文件已经存在,则会覆盖已有文件的内容。</strong>
* <p>
* 以下是两个使用的例子:
* <p>
* <pre>
* CompressUtils.gzCompress("foo.txt", new CompressHandler() {
* @Override
* public void handle(OutputStream out) throws IOException {
* out.write("This is a test.".getBytes());
* }
* });
* </pre>
* <p>
* <pre>
* CompressUtils.gzCompress("bar.txt", new CompressHandler() {
* @Override
* public void handle(OutputStream out) throws IOException {
* BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
* writer.write("This is a test.");
* writer.flush(); // 刷新缓冲
* }
* });
* </pre>
*
* @param dstFile 目标文件,即压缩后的文件
* @param handler 压缩输出流处理类
* @throws IOException 当压缩过程中发生错误等情况下抛出此异常
*/
public static void gzCompress(String dstFile, CompressHandler handler) throws IOException {
if (!dstFile.endsWith(".gz")) {
dstFile = dstFile + ".gz";
}
GZIPOutputStream gzos = null;
FileChannel channel = null;
FileLock lock = null;
try {
FileOutputStream fos = new FileOutputStream(dstFile);
channel = fos.getChannel();
lock = channel.lock();
gzos = new GZIPOutputStream(fos);
handler.handle(gzos);
} catch (IOException e) {
throw new IOException("Error occurred while compressing stream into [" + dstFile + "].", e);
} finally {
clean(null, gzos, lock, channel);
}
}
/**
* 对指定的文件以 GZip 方式进行压缩。<strong>注意:如果目标文件已经存在,则会覆盖已有文件的内容。</strong>
*
* @param srcFile 源文件,即待压缩的文件
* @param dstFile 目标文件,即压缩后的文件
* @throws IOException 当源文件不存在、压缩过程中发生错误等情况下抛出此异常
*/
public static void gzCompress(String srcFile, String dstFile) throws IOException {
File file2Compress = new File(srcFile);
if (!file2Compress.exists()) {
throw new IOException("The file to compress named [" + srcFile + "] does not exist.");
}
if (!dstFile.endsWith(".gz")) {
dstFile = dstFile + ".gz";
}
FileInputStream fis = null;
GZIPOutputStream gzos = null;
FileChannel channel = null;
FileLock lock = null;
try {
FileOutputStream fos = new FileOutputStream(dstFile);
// 取得文件锁,保证在写文件过程中不会被其他进程读到不完整的数据
channel = fos.getChannel();
lock = channel.lock();
fis = new FileInputStream(srcFile);
gzos = new GZIPOutputStream(fos);
byte[] buffer = new byte[BUFFER_SIZE];
for (int len = 0; (len = fis.read(buffer)) != -1; ) {
gzos.write(buffer, 0, len);
}
} catch (IOException e) {
throw new IOException("Error occurred while compressing [" + srcFile + "] into [" + dstFile + "].", e);
} finally {
clean(fis, gzos, lock, channel);
}
}
/**
* 对指定的文件以 GZip 方式进行解压缩。<strong>注意:如果目标文件已经存在,则会覆盖已有文件的内容。</strong>
*
* @param srcFile 源文件,即待解压的文件
* @param dstFile 目标文件,即解压后的文件
* @throws IOException 当源文件不存在、解压缩过程中发生错误等情况下抛出此异常
*/
public static void gzDecompress(String srcFile, String dstFile) throws IOException {
File compressedFile = new File(srcFile);
if (!compressedFile.exists()) {
throw new IOException("The file to decompress named [" + srcFile + "] does not exist.");
}
GZIPInputStream gzis = null;
FileOutputStream fos = null;
try {
gzis = new GZIPInputStream(new FileInputStream(srcFile));
fos = new FileOutputStream(dstFile);
byte[] buffer = new byte[BUFFER_SIZE];
for (int len = 0; (len = gzis.read(buffer)) != -1; ) {
fos.write(buffer, 0, len);
}
} catch (IOException e) {
throw new IOException("Error occurred while decompressing [" + srcFile + "] into [" + dstFile + "].", e);
} finally {
clean(gzis, fos);
}
}
/**
* 对指定的文件以 Zip 方式进行压缩,目前只支持单个文件,不支持对目录进行压缩操作。
* <p>
* <strong>注意:如果目标文件已经存在,则会覆盖已有文件的内容。</strong>
*
* @param srcFile 源文件,即待压缩的文件
* @param dstFile 目标文件,即压缩后的文件
* @throws IOException 当源文件不存在、压缩过程中发生错误等情况下抛出此异常
*/
public static void zipCompress(String srcFile, String dstFile) throws IOException {
File file2Compress = new File(srcFile);
if (!file2Compress.exists()) {
throw new IOException("The file to compress named [" + srcFile + "] does not exist.");
}
if (!dstFile.endsWith(".zip")) {
dstFile = dstFile + ".zip";
}
FileInputStream fis = null;
ZipOutputStream zos = null;
FileChannel channel = null;
FileLock lock = null;
try {
FileOutputStream fos = new FileOutputStream(dstFile);
channel = fos.getChannel();
lock = channel.lock();
fis = new FileInputStream(srcFile);
zos = new ZipOutputStream(fos);
ZipEntry entry = new ZipEntry(file2Compress.getName());
entry.setCompressedSize(file2Compress.length());
entry.setTime(file2Compress.lastModified());
zos.putNextEntry(entry);
byte[] buffer = new byte[BUFFER_SIZE];
for (int len = 0; (len = fis.read(buffer)) != -1; ) {
zos.write(buffer, 0, len);
}
} catch (IOException e) {
throw new IOException("Error occurred while compressing [" + srcFile + "] into [" + dstFile + "].");
} finally {
clean(fis, zos, lock, channel);
}
}
/**
* 对指定的文件以 Zip 方式进行解压缩。目前只支持单个文件,不支持对目录进行解压操作。
* <p>
* <strong>注意:如果目标文件已经存在,则会覆盖已有文件的内容。</strong>
*
* @param srcFile 源文件,即待解压的文件
* @param dstFile 目标文件,即解压后的文件
* @throws IOException 当源文件不存在、解压缩过程中发生错误等情况下抛出此异常
*/
public static void zipDecompress(String srcFile, String dstFile) throws IOException {
File compressedFile = new File(srcFile);
if (!compressedFile.exists()) {
throw new IOException("The file to decompress named [" + srcFile + "] does not exist.");
}
ZipInputStream zis = null;
FileOutputStream fos = null;
try {
zis = new ZipInputStream(new FileInputStream(srcFile));
fos = new FileOutputStream(dstFile);
ZipEntry entry = zis.getNextEntry();
if (entry == null) {
throw new IOException("The file to decompress named [" + srcFile + "] has no zip entry.");
}
byte[] buffer = new byte[BUFFER_SIZE];
for (int len = 0; (len = zis.read(buffer)) != -1; ) {
fos.write(buffer, 0, len);
}
} catch (IOException e) {
throw new IOException("Error occurred while decompressing [" + srcFile + "] into [" + dstFile + "].");
} finally {
clean(zis, fos);
}
}
/**
* 释放资源,关闭输入输出流。
*/
private static void clean(InputStream in, OutputStream out) throws IOException {
FileUtils.close(in);
FileUtils.close(out);
}
/**
* 释放资源,包括关闭输入输出流、关闭文件通道、释放文件锁。
*/
private static void clean(InputStream in, OutputStream out, FileLock lock, FileChannel channel) throws IOException {
FileUtils.close(in);
// 释放文件锁
if (lock != null) {
lock.release();
}
// 在关闭压缩输出流之后再关闭通道,如果先关闭通道会导致 压缩文件的格式错误
FileUtils.close(out);
if (channel != null) {
channel.close();
}
}
}