最近又接到了实验室的新任务,做文件的批量上传,批量下载。大致需要实现以下几项功能:
1.可以批量选择待上传的文件
2.可以上传大文件
3.能够让用户看到公有云盘的文件列表,私有云盘的文件列表
4.能够让用户以复选的形式批量下载文件,借鉴于各种流行的网盘批量下载文件时选择的解决方案:将用户选择的文件打包下载。
很开心的表示,我完成了所有的要求,其中不乏从白痴慢慢摸索,磕磕绊绊,但总归圆满。这次来讲讲java实现的文件打包,这次学习到的其它知识和经验,等有空再记下来,免得以后忘记了。
其实java是很面向对象的语言,强大到爆的工具类和API,以至于至今我还不能窥其一二。java.util.zip包中完整的提供了关于文件压缩和解压的工具类和方法,网上流行着各种apache的第三方工具库,异常强大,但我不喜欢引入第三方的工具,究其原因就是很难查到这些工具的引入方法,网上基本上都不讲,上来就是例子代码,各种import完事,看了以后一头雾水,主要是太菜了。所以就自己依靠官方提供的工具,自己慢慢的摸索。
首先,整理一下文件打包逻辑,然后,贴上源码
1.要获得需要打包的文件
//e.g: File file = new File("test.txt");
2.生成最终打包的目标文件
//e.g: File zipFile = new File("DownLoad.zip");//最终打包的压缩包
3.要往压缩包里面写数据,就要使用特别为写压缩包定制的输出流ZipOutputStream,将这个输出流的输出端套在压缩包File变量上,就像把水管套进蓄水池中一样
ZipOutputStream zipStream = new ZipOutputStream(new FileOutputStream(zipFile));//用这个构造最终压缩包的输出流
4.要把提供数据的源头文件的内容读出来,存储在缓冲字节数组里面,使用缓冲技术加快文件读写,关于文件读取,这个很基本了(建FileInputStream流,外层再套上BufferedInputStream流)
FileInputStream zipSource = new FileInputStream(file); byte[] bufferArea = new byte[1024 * 10];//读写缓冲区 BufferedInputStream bufferStream = new BufferedInputStream(zipSource, 1024 * 10);//输入缓冲流 int read = 0; while((read = bufferStream.read(bufferArea, 0, 1024 * 10)) != -1) { }
5.有了byte[]数组,就可以使用ZipOutputStream.write(byte[] b, int off, int len)方法写入压缩包了,但是压缩包是压缩多文件使用的,它具有文件列表,必须要告诉当前应该写到哪一个条目里面,就像图书馆放书到书架上,必须要指定这本书应该放在哪一个书架,ZipEntry就是干这个活的
//压缩条目不是具体独立的文件,而是压缩包文件列表中的列表项,称为条目,就像索引一样 ZipEntry zipEntry = new ZipEntry(file1.getName()); zipStream.putNextEntry(zipEntry);//定位到该压缩条目位置,开始写入文件到压缩包中
6.最后一步,开始使用byte[]数组中存储的已读取信息,向压缩包写数据
//在任何情况下,b[0] 到 b[off] 的元素以及 b[off+len] 到 b[b.length-1] 的元素都不会受到影响。这个是官方API给出的read方法说明,经典! while((read = bufferStream.read(bufferArea, 0, 1024 * 10)) != -1) { zipStream.write(bufferArea, 0, read); }
至此,文件压缩逻辑备忘完毕,但还得多写几个要点,都是很细节的领悟,以免忘记了没处查
1.关于文件操作中有形形色色的read,write操作,java允许方法重写,这苦了我这样的菜鸟,有点晕,但是使用的时候都一样的名字还是挺便利的。
InputStream应该是所有的输入流的鼻祖了,它共定义了3种read方法,各有千秋,官方的API介绍的很专业,非常经典,我常常对于一些细节拿捏不准,今天好好整理一下
1.1. int read():从输入流中读取数据的下一个字节。返回 0
到 255
范围内的 int
字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。
返回:下一个数据字节;如果到达流的末尾,则返回 -1
。
1.2. int read(byte[] b):从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b
中。以整数形式返回实际读取的字节数。如果 b
的长度为 0,则不读取任何字节并返回 0
;否则,尝试读取至少一个字节。如果因为流位于文件末尾而没有可用的字节,则返回值 -1
;否则,至少读取一个字节并将其存储在 b
中。将读取的第一个字节存储在元素 b[0]
中,下一个存储在 b[1] 中,依次类推。读取的字节数最多等于 b 的长度。设 k 为实际读取的字节数;这些字节将存储在 b[0]
到 b[
k-1]
的元素中,不影响 b[
k]
到 b[b.length-1]
的元素。类 InputStream 的 read(b) 方法的效果等同于:read(b, 0, b.length);
返回:读入缓冲区的总字节数;如果因为已经到达流末尾而不再有数据可用,则返回 -1
。
1.3. int read(byte[] b, int off, int len):这个得好好说说,将输入流中最多 len
个数据字节读入 byte 数组。尝试读取 len
个字节,但读取的字节也可能小于该值。以整数形式返回实际读取的字节数。如果 len
为 0,则不读取任何字节并返回 0
;否则,尝试读取至少一个字节。如果因为流位于文件末尾而没有可用的字节,则返回值 -1
;否则,至少读取一个字节并将其存储在 b
中。将读取的第一个字节存储在元素 b[off]
中,下一个存储在 b[off+1]
中,依次类推。读取的字节数最多等于 len
。设 k 为实际读取的字节数;这些字节将存储在 b[off]
到 b[off+
k-1]
的元素中,不影响 b[off+
k]
到 b[off+len-1]
的元素。
在任何情况下,b[0]
到 b[off]
的元素以及 b[off+len]
到 b[b.length-1]
的元素都不会受到影响。
- 参数:
b
- 读入数据的缓冲区。off
- 数组b
中将写入数据的初始偏移量。不是待读取流的开始读取位置,别再记错了len
- 要读取的最大字节数。- 返回:读入缓冲区的总字节数;如果因为已到达流末尾而不再有数据可用,则返回
-1
。 - OutputStream应该是所有的输出流的鼻祖了,它共定义了3种write方法,今天也好好整理一下
- 1.4. void write(int b): 将指定的字节写入此输出流。
write
的常规协定是:向输出流写入一个字节。要写入的字节是参数b
的八个低位。b
的 24 个高位将被忽略。 - 参数:
b
-字节
- 1.5. void write(byte[] b): 将
b.length
个字节从指定的 byte 数组写入此输出流。write(b)
的常规协定是:应该与调用write(b, 0, b.length)
的效果完全相同。 - 1.6. void write(byte[] b, int off, int len): 将指定 byte 数组中从偏移量
off
开始的len
个字节写入此输出流。write(b, off, len)
的常规协定是:将数组b
中的某些字节按顺序写入输出流;元素b[off]
是此操作写入的第一个字节,b[off+len-1]
是此操作写入的最后一个字节。OutputStream
的write
方法对每个要写出的字节调用一个参数的 write 方法。如果b
为null
,则抛出NullPointerException
。如果off
为负,或len
为负,或者off+len
大于数组b
的长度,则抛出 IndexOutOfBoundsException。 - 参数:
b
- 数据。 off
- 数据中的初始偏移量。不是写入流的开始写入位置,别记错了len
- 要写入的字节数。- 2.关于文件压缩形成的压缩包,经过测试可以重复使用,即在某一个文件夹下建立了一个程序生成的压缩包,它包含了一些文件a,b,c.当程序下次运行时,发现还可以用这个压缩包名字,去压缩新的文件集合d,e,f,当然原来的压缩包直接被替换重写掉了。这是一个很不错的好处,可以大大减少服务器硬盘负载,由于我做的文件下载支持多用户,并分公有盘和私有盘,也许某一时刻会有很多用户同时批量下载文件,所以,可以把压缩包名称命名为DownLoad_userName_Private.zip,DownLoad_userName_Public.zip。即支持多用户同时下载公有盘私有盘,但不允许同一个用户对同一个类型的云盘进行一次以上同时的下载操作【因为zip文件会被替换,但是原有的可能还在下载中】
- 啰嗦了这么多,只怕以后忘了,记忆力要是有云能力就好了,贴上打包文件的java全代码:
import java.io.*; import java.util.zip.*; public class FileZip { /** * @param args */ //压缩已完成!bingo public static void main(String[] args) { // TODO Auto-generated method stub File file = new File("22.doc"); File zipFile = new File("DownLoad.zip");//最终打包的压缩包 ZipOutputStream zipStream = null; FileInputStream zipSource = null; BufferedInputStream bufferStream = null; try { zipStream = new ZipOutputStream(new FileOutputStream(zipFile));//用这个构造最终压缩包的输出流 zipSource = null;//将源头文件格式化为输入流 zipSource = new FileInputStream(file1); byte[] bufferArea = new byte[1024 * 10];//读写缓冲区 //压缩条目不是具体独立的文件,而是压缩包文件列表中的列表项,称为条目,就像索引一样 ZipEntry zipEntry = new ZipEntry(file1.getName()); zipStream.putNextEntry(zipEntry);//定位到该压缩条目位置,开始写入文件到压缩包中 bufferStream = new BufferedInputStream(zipSource, 1024 * 10);//输入缓冲流 int read = 0; //在任何情况下,b[0] 到 b[off] 的元素以及 b[off+len] 到 b[b.length-1] 的元素都不会受到影响。这个是官方API给出的read方法说明,经典! while((read = bufferStream.read(bufferArea, 0, 1024 * 10)) != -1) { zipStream.write(bufferArea, 0, read); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { //关闭流 try { if(null != bufferStream) bufferStream.close(); if(null != zipStream) zipStream.close(); if(null != zipSource) zipSource.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
发表评论 取消回复