最近在做一个需求:从其他系统的ftp目录下载存储图片url的文件,然后读取文件中的url地址,根据地址下载图片后按天压缩成一个包,平均一个地址文件中包含4000个地址左右,也就是说一个文件扫描后需要下载4000个左右的图片,然后压缩,下面把我的实现方式和优化过程记录下来,如果大家有什么更好的方式可以分享。

使用框架:SpringMVC

定时任务实现:继承org.springframework.scheduling.quartz.QuartzJobBean;

ftp环境搭建就不说了,在其他博客记录过,使用虚拟机中的CentOS搭建的FTP服务,创建FTP账号及对应目录,事先上传需要下载的图片地址文件。文件内容格式“图片ID||图片地址”。

方法一、最简单的实现方法就是先下载存储图片url地址的文件,然后读取文件遍历图片地址,调下载图片的方法将图片存储到本地,最后压缩下载的图片,完成后删除下载的图片,只保留压缩包。

public class PictureTransferJob extends QuartzJobBean

{

protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException

{

//实际的FTP配置是读取配置文件获取的

//FTP地址

String hostName ="192.168.1.112";

//FTP端口

int port = 2001;

/FTP账号

String userName = "test1";

//ftp密码

String password = "test1";

//ftp文件存储目录

String ftpDowload = "/";

//文件本地存储路径

String path = this.getClass().getResource("/").getPath();

//图片地址文件存储目录

String addrPath=path.substring(1, path.indexOf("WEB-INF/classes"))+"picAddr";

//实际下载的图片存储目录

String picPath=path.substring(1, path.indexOf("WEB-INF/classes"))+"pic";

addrPath = addrPath.replace("%20"," ");

picPath = picPath.replace("%20"," ");

try

{

//创建存储图片地址的文件

creatFile(addrPath);

//创建存储实际图片的文件

creatFile(picPath);

String oldAddrPath = addrPath;

String oldPicPath = picPath;

//创建FTP连接

FtpUtil2 ftpUtil2 = new FtpUtil2(hostName, port,userName, password, ftpDowload, true);

//遍历FTP目录下的文件

String[] files = ftpUtil2.ListAllFiles();

//本地数据库会有一个表记录下载过的文件,这里会查询数据库和ftp列出的文件名比较,如果已经下载过的文件就不会下载,避免重复下载。

//下面省略比较的过程,循环files数组,在本地创建文件

for(int i=0;i

creatFile(addrPath+File.separator+fileName);

//ftpDowload是ftp服务器存储文件的地址,addrPath是本地存储文件的地址

//这里一个返回状态判断文件是否下载成功

boolean downloadInvestorFlag = ftpUtil2.downloadFile(ftpDowload, addrPath);

//文件下载成功后调读取文件的方法,将需要下载的图片地址存入容器

boolean entityState = setPictureDetail(addrPath,picPath,fileNameDate);

}

}

catch (Exception e)

{

e.printStackTrace();

//调记录错误日志的业务类用于发送下载文件出错的短信

}

}

//这里开始读图片地址

private boolean setPictureDetail(String addrPath,String picPath,String synDate)

{

System.out.println("----------进入setPictureDetail方法-----------");

BufferedReader br = null;

try

{

br=new BufferedReader(new InputStreamReader(new FileInputStream(addrPath),"UTF-8"));

String row;

int count=0;

//map中存储每行读取到的图片名称和URL地址

Map addrMap=new HashMap();

while ((row=br.readLine())!=null)

{

try

{

count++;

if (count==1)

{

continue;

}

String[] column = row.split("\\|\\|", -1);

addrMap.put(column[0].trim(), column[1].trim());

}

catch (Exception e)

{

e.printStackTrace();

}

}

System.out.println(new Date());

//这里调用压缩方法,压缩方法中会调用执行下载图片的方法

zipPic(picPath,synDate,addrMap);

System.out.println(new Date());

System.out.println("----------完成--------------");

return true;

}

catch (Exception e)

{

e.printStackTrace();

//调用记录错误日志的业务类

return false;

}finally {

try {

if (null != br)

br.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

/**

* 根据url地址下载图片

* @throws IOException

*/

private boolean downPic(String picPath,List> addrList,List picList)throws IOException{

InputStream is=null;

FileOutputStream fos=null;

URL url=null;

String fileName=null;

String picAddr=null;

File pic=null;

try

{

for(Map.Entry addrEntry:addrList)

{

fileName=addrEntry.getKey();

picAddr=addrEntry.getValue();

//创建Url对象

url=new URL(picAddr);

is=url.openStream();

//URLConnection获取到的流通过InputStream直接写入字节数组会缺失数据,导致下载的图片不完整,使用org.apache.commons.io.IOUtils.toByteArray(urlconnection.openstream())可以解决

byte[] bytes=IOUtils.toByteArray(is);//new byte[is.available()];获取的字节

//流中数据读入字节数组,读入后,流中数据清空

pic=new File(picPath+fileName+".jpg");

fos=new FileOutputStream(pic);

fos.write(bytes);

//将下载的图片存入List,待图片全部下载完成后传入zip方法进行压缩

picList.add(pic);

fos.flush();

fos.close();

is.close();

}

return true;

}

catch (Exception e)

{

e.printStackTrace();

return false;

}

finally{

if (null!=fos)

{

fos.close();

}

if (null!=is)

{

is.close();

}

}

}

//这里是压缩文件的伪代码

private void zipPic(picPath,synDate,addrMap);{

//传入需要压缩的文件列表和压缩文件名

ZipUtil.zipByStream(picList,new File(picPath+synDate+".zip"));

}

/**

* 创建文件

* @param path

*/

private void creatFile(String path)

{

File file = new File(path);

if(!file.exists())

{

file.mkdirs();

}

}

}

方法二、多线程下载、直接压缩流

方法一虽然实现了基本功能,但是由于需要下载的图片太多,以及压缩本地图片文件和删除图片也比较耗时,所以可以优化速度的地方有两个。一个就是提高下载图片的效率,一个就是提高压缩的效率。

提高下载效率的方法可以使用多线程下载,提高压缩效率的方法是可以不将图片保存到本地而直接压缩文件流。

多线程实现方式:首先我们保存了需要下载的文件的地址列表,我们要使用多线程下载就要保证不同线程下载的图片不会重复,因此需要一个标志来区分,这时就可以使用一个索引计数器,按每个线程下载一定量图片分割,从0开始,每隔比如400个图片就用一个线程下载,这样就可以确定需要的线程个数,并且每个线程下载的图片不会重复。

压缩文件实现方式:因为生成压缩文件的本质也是读取需要压缩的文件流,然后生成压缩包,因此我们可以不创建下载的图片文件,而直接使用容器存储所有线程下载的图片流数据,然后将流数据传给压缩工具类直接压缩,这样就省略了读取图片文件创建流,然后生成压缩包,再删除本地图片文件的繁琐过程。

下面列出改造的主要实现:

/**

* 将下载的图片按天压缩

* @throws IOException

*/

private boolean zipPic(String picPath,String synDate,Map addrMap) throws IOException{

//这里由于是多线程存储图片流,所以需要使用线程安全的map,因此使用ConcurrentHashMap

Map pictureList=new ConcurrentHashMap();

//这里定义每个线程下载的图片个数

int count=400;

//存储需要下载的图片地址

List> addrList=new ArrayList>(addrMap.entrySet());

//线程数,加一是因为要创建一个线程下载最后不足400个的图片

int nThreads=(addrList.size()/count)+1;

//CountDownLatch countDownLatch = new CountDownLatch(nThreads);

try

{

boolean downPic=false;

//执行多线程下载图片

downPic=downPic(picPath,addrList,picList,pictureList,nThreads,count);

if (downPic)

{

ZipUtil.zipByArray(picList,new File(picPath+synDate+".zip"));

}

return true;

}

catch (Exception e)

{

e.printStackTrace();

return false;

}

}

下面是创建线程池

/**

* 根据url地址下载图片

* @throws InterruptedException

*/

private boolean downPic(String picPath,List> addrList,Map picList,Map pictureList,int nThreads,int count)throws IOException, InterruptedException{

ExecutorService threadPool=Executors.newFixedThreadPool(nThreads);

// 创建两个个计数器

CountDownLatch begin=new CountDownLatch(0);

CountDownLatch end=new CountDownLatch(nThreads);

// 循环创建线程

for (int i = 0; i < nThreads; i++) {

List>subAddrList=null;

// 计算每个线程执行的数据

if ((i + 1) == nThreads) {

int startIndex = (i * count);

int endIndex = addrList.size();

subAddrList = addrList.subList(startIndex, endIndex);

} else {

int startIndex = (i * count);

int endIndex = (i + 1) * count;

subAddrList = addrList.subList(startIndex, endIndex);

}

// 线程类

PicDownload mythead = new PicDownload(picPath,subAddrList,picList,pictureList);

// 这里执行线程的方式是调用线程池里的threadPool.execute(mythead)方法。

try

{

threadPool.execute(mythead);

}

catch (Exception e)

{

//记录错误日志

return false;

}

}

begin.countDown();

end.await();

// 执行完关闭线程池

threadPool.shutdown();

//这里一定要循环直到线程池中所有线程都结束才能往下走,测试时由于没有这一步导致子线程下载图片还没完成,而主线程已经往下走了,导致压缩包内没有图片

//也可以使用CountDownLatch实现

/*while (true)

{

if (threadPool.isTerminated())

{

System.out.println("所有子线程已结束!");

break;

}

}*/

return true;

}

下面是线程实现

class PicDownload implements Runnable{

//下载图片的地址列表

List> addrList;

//装载下载成功的图片列表

Map picList;

Map pictureList;

//图片本地存储路径

String picPath;

CountDownLatch begin,end;

public PicDownload(String picPath,List> addrList,Map picList,CountDownLatch begin,CountDownLatch end){

this.addrList=addrList;

this.picList=picList;

this.picPath=picPath;

this.begin=begin;

this.end=end;

}

@Override

public void run()

{

try

{

System.out.println(Thread.currentThread().getName()+"------"+Thread.currentThread().getId());

downPicture(addrList);

//System.out.println(countDownLatch.getCount());

begin.await();

}

catch (Exception e)

{

e.printStackTrace();

}finally{

end.countDown();

//countDownLatch.countDown();

}

}

public boolean downPicture(List> addrList) throws Exception{

InputStream is=null;

FileOutputStream fos=null;

URL url=null;

String fileName=null;

String picAddr=null;

File pic=null;

try

{

for(Map.Entry addrEntry:addrList)

{

fileName=addrEntry.getKey();

picAddr=addrEntry.getValue();

//创建Url对象

url=new URL(picAddr);

is=url.openStream();

//URLConnection获取到的流通过InputStream直接写入字节数组会缺失数据,导致下载的图片不完整,使用org.apache.commons.io.IOUtils.toByteArray(urlconnection.openstream())可以解决

//byte[] bytes=IOUtils.toByteArray(is);//new byte[is.available()];获取的字节

//流中数据读入字节数组,读入后,流中数据清空

picList.put(fileName+".jpg", is);

//这时候由于没有把流写入文件,一定不能关闭流,否则流中的数据就会丢失

//is.close();

}

return true;

}

catch (Exception e)

{

e.printStackTrace();

return false;

}

finally{

//不能关闭流

/*if (null!=is)

{

is.close();

}*/

}

}

}

上面使用流来压缩遇到了另一个问题,在压缩文件时会出现java.net.SocketException:Connection reset

分析了一下原因,应该是由于流InputStream和UrlConnection是连接状态的,UrlConnection超时重置导致了获取输入流失败。

尝试设置URLConnection的超时时间,但是测试时发现图片下载收到网速影响较大,这种方式很不稳定,不可取,最后只有放弃使用流,而改用字节数组传给压缩工具类,然后将字节数组转为流压缩。

/**

*使用容器存储下载的图片字节数组

*/

public boolean downPicture(List> addrList) throws Exception{

InputStream is=null;

FileOutputStream fos=null;

URL url=null;

String fileName=null;

String picAddr=null;

File pic=null;

try

{

for(Map.Entry addrEntry:addrList)

{

fileName=addrEntry.getKey();

picAddr=addrEntry.getValue();

//创建Url对象

url=new URL(picAddr);

//打开连接,创建java.net.URLConnection对象,该对象没有关闭连接的方法,可以转为它的子类HttpURLConnection调用disconnect方法关闭连接。

//java.net.URLConnection和java.net.HttpURLConnection都有设置超时时间的方法关闭连接

//HttpURLConnection uc=(HttpURLConnection)url.openConnection();

is=uc.getInputStream();

//URLConnection获取到的流通过InputStream直接写入字节数组会缺失数据,导致下载的图片不完整,使用org.apache.commons.io.IOUtils.toByteArray(urlconnection.openstream())可以解决

byte[] bytes=IOUtils.toByteArray(is);//new byte[is.available()];获取的字节

//流中数据读入字节数组,读入后,流中数据清空

//is.read(bytes);

picList.put(fileName+".jpg",bytes);

is.close();

}

return true;

}

catch (Exception e)

{

e.printStackTrace();

return false;

}

finally{

if (null!=is)

{

is.close();

}

}

}

总结:

实现过程中遇到的问题:

1、使用线程池时对于共享状态,比如这里的存储下载的图片字节数据容器是所有线程共享的,因此需要使用同步的容器,否则会导致存储的数据出问题,因此使用了ConcurrentHashMap

2、这里存在一个主线程和子线程的执行顺序问题,因为主线程需要等待线程池中所有线程下载图片结束后才能往下走去压缩图片,如果主线程不等待子线程结束就向下执行压缩方法就会导致压缩图片缺少或者没有压缩图片。因此可以使用CountDownLatch实现,或者在关闭线程池语句下面使用死循环检查threadPool.isTerminated()才能继续执行主线程去压缩图片。

3、由于直接将UrlConnection获取到的输入流直接传给压缩类进行压缩,存在连接超时重置的情况,因此改用将下载的流存入字节数组,再传给压缩类压缩,避免使用流出现意外情况。

4、在使用urlconnection.openStream()获取输入流后,转换为字节数组下载的图片是不完整的。。使用org.apache.commons.io.IOUtils.toByteArray(urlconnection.openstream())可以解决,具体可以阅读其源码看实现。

下面是FTP工具类的实现:

import java.io.BufferedInputStream;

import java.io.BufferedOutputStream;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import org.apache.commons.net.ftp.FTPClient;

import org.apache.commons.net.ftp.FTPClientConfig;

import org.apache.commons.net.ftp.FTPConnectionClosedException;

import org.apache.commons.net.ftp.FTPReply;

public class FtpUtil2 {

private FTPClient ftpClient = null;

// ftp服务器地址

private String hostName;

// ftp服务器默认端口

public static int defaultport = 21;

// 登录名

private String userName;

// 登录密码

private String password;

// 需要访问的远程目录

private String remoteDir;

/**

* @param hostName

* 主机地址

* @param port

* 端口号

* @param userName

* 用户名

* @param password

* 密码

* @param remoteDir

* 默认工作目录

* @param is_zhTimeZone

* 是否是中文FTP Server端

* @return

* @return

*/

/**

* 新增方法

*/

public FtpUtil2()

{

PropConfig config = PropConfig.loadConfig("system.properties");

String hostName = config.getConfig("ftpAddress");

String port = config.getConfig("ftpPort");

String userName = config.getConfig("ftpUserName");

String password = config.getConfig("ftpPassword");

String remoteDir = config.getConfig("remoteFilePath");

boolean is_zhTimeZone= true;

this.hostName = hostName;

this.userName = userName;

this.password = password;

this.remoteDir = remoteDir == null ? "" : remoteDir;

this.ftpClient = new FTPClient();

if (is_zhTimeZone) {

this.ftpClient.configure(FtpUtil2.Config());

this.ftpClient.setControlEncoding("GBK");

}

// 登录

this.login();

// 切换目录

this.changeDir(this.remoteDir);

this.setFileType(FTPClient.BINARY_FILE_TYPE);

ftpClient.setDefaultPort(Integer.parseInt(port));

}

public FtpUtil2(String hostName, int port, String userName,

String password, String remoteDir, boolean is_zhTimeZone) {

this.hostName = hostName;

this.userName = userName;

this.password = password;

defaultport=port;

this.remoteDir = remoteDir == null ? "" : remoteDir;

this.ftpClient = new FTPClient();

if (is_zhTimeZone) {

this.ftpClient.configure(FtpUtil2.Config());

this.ftpClient.setControlEncoding("GBK");

}

// 登录

this.login();

// 切换目录

this.changeDir(this.remoteDir);

this.setFileType(FTPClient.ASCII_FILE_TYPE);

ftpClient.setDefaultPort(port);

}

/**

* 登录FTP服务器

*/

public boolean login() {

boolean success = false;

try {

ftpClient.connect(this.hostName,defaultport);

ftpClient.login(this.userName, this.password);

int reply;

reply = ftpClient.getReplyCode();

if (!FTPReply.isPositiveCompletion(reply)) {

ftpClient.disconnect();

return success;

}

} catch (FTPConnectionClosedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

success = true;

System.out.println("连接到ftp服务器:" + this.hostName + " 成功..开始登录");

return success;

}

private static FTPClientConfig Config() {

FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);

conf.setRecentDateFormatStr("MM月dd日 HH:mm");

// conf.setRecentDateFormatStr("(YYYY年)?MM月dd日( HH:mm)?");

return conf;

}

/**

* 变更工作目录

*

* @param remoteDir

*

*/

public void changeDir(String remoteDir) {

try {

this.remoteDir = remoteDir;

ftpClient.changeWorkingDirectory(remoteDir);

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println("变更工作目录为:" + remoteDir);

}

/**

* 返回上一级目录(父目录)

*/

public void toParentDir() {

try {

ftpClient.changeToParentDirectory();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

/**

* 列出当前工作目录下所有文件

*/

public String[] ListAllFiles() {

String[] names = this.ListFiles("*");

return this.sort(names);

}

/**

* 列出指定工作目录下的匹配文件

*

* @param dir

* exp: /cim/

* @param file_regEx

* 通配符为*

*/

public String[] ListAllFiles(String dir, String file_regEx) {

String[] names = this.ListFiles(dir + file_regEx);

return this.sort(names);

}

/**

* 列出匹配文件

*

* @param file_regEx

* 匹配字符,通配符为*

*/

public String[] ListFiles(String file_regEx) {

try {

/**

* FTPFile[] remoteFiles = ftpClient.listFiles(file_regEx);

* //System.out.println(remoteFiles.length); String[] name = new

* String[remoteFiles.length]; if(remoteFiles != null) { for(int

* i=0;i

* name[i] = ""; else

* if(remoteFiles[i].getName()==null||remoteFiles

* [i].getName().equals

* (".")||remoteFiles[i].getName().equals("..")) { name[i] = "";

* } else name[i] = remoteFiles[i].getName();

* System.out.println(name[i]); } }

*/

ftpClient.enterLocalPassiveMode();

String[] name = ftpClient.listNames(file_regEx);;

if (name == null)

return new String[0];

return this.sort(name);

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return new String[0];

}

public void Lists(String reg) {

try {

String[] a = ftpClient.listNames(reg);

if (a != null) {

for (String b : a) {

System.out.println(b);

}

}

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

/**

* 设置传输文件的类型[文本文件或者二进制文件]

*

* @param fileType

* --BINARY_FILE_TYPE,ASCII_FILE_TYPE

*/

public void setFileType(int fileType) {

try {

ftpClient.setFileType(fileType);

} catch (IOException e) {

e.printStackTrace();

}

}

/**

* 上传文件

*

* @param localFilePath

* --本地文件路径+文件名

* @param newFileName

* --新的文件名

*/

public void uploadFile(String localFilePath, String newFileName) {

// 上传文件

this.ftpClient.enterLocalPassiveMode();// 被动模式连接

BufferedInputStream buffIn = null;

try {

buffIn = new BufferedInputStream(new FileInputStream(localFilePath));

boolean ifUpload = ftpClient.storeFile(newFileName, buffIn);

if (!ifUpload) {

System.out.println("上传文件失败。。。");

} else {

System.out.println("上传文件成功。。。");

}

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

if (buffIn != null)

buffIn.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

/**

* 上传文件2

*

* @param file

* --FileInputStream的文件

* @param newFileName

* --新的文件名

*/

public void newUploadFile(FileInputStream file, String newFileName) {

// 上传文件

this.ftpClient.enterLocalPassiveMode();// 被动模式连接

BufferedInputStream buffIn = null;

try {

buffIn = new BufferedInputStream(file);

boolean ifUpload = ftpClient.storeFile(newFileName, buffIn);

if (!ifUpload) {

System.out.println("上传文件失败。。。");

} else {

System.out.println("上传文件成功。。。");

}

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

if (buffIn != null)

buffIn.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

/**

* 下载文件(单个)

*

* @param remoteFileName

* --服务器上的文件名

* @param localFileName

* --本地文件名

*/

public boolean downloadFile(String remoteFileName, String localFileName) {

this.ftpClient.enterLocalPassiveMode();// 被动模式连接

BufferedOutputStream buffOut = null;

try {

buffOut = new BufferedOutputStream(new FileOutputStream(

localFileName));

boolean ifDownload = ftpClient

.retrieveFile(remoteFileName, buffOut);

if (!ifDownload) {

System.out.println("下载文件失败。。。");

return false;

} else {

System.out.println("下载文件成功。。。");

}

} catch (Exception e) {

e.printStackTrace();

return false;

} finally {

try {

if (buffOut != null)

buffOut.close();

} catch (Exception e) {

e.printStackTrace();

}

}

return true;

}

/**

* 关闭FTP连接

*/

public void close() {

try {

if (ftpClient != null) {

ftpClient.logout();

ftpClient.disconnect();

}

} catch (Exception e) {

e.printStackTrace();

}

}

/**

* 冒泡排序字符串(从大到小)

*/

public String[] sort(String[] str_Array) {

if (str_Array == null) {

throw new NullPointerException("The str_Array can not be null!");

}

String tmp = "";

for (int i = 0; i < str_Array.length; i++) {

for (int j = 0; j < str_Array.length - i - 1; j++) {

if (str_Array[j].compareTo(str_Array[j + 1]) < 0) {

tmp = str_Array[j];

str_Array[j] = str_Array[j + 1];

str_Array[j + 1] = tmp;

}

}

}

return str_Array;

}

public static void main(String[] strs) {

FtpUtil2 FtpUtil2 = new FtpUtil2("192.168.1.112", 20011, "test1",

"test1", "/", true);

FtpUtil2.downloadFile("test.txt", "d:\\test.txt");

}

}

下面是ZIP工具类:

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.util.ArrayList;

import java.util.Enumeration;

import java.util.List;

import java.util.Map;

import java.util.zip.ZipEntry;

import java.util.zip.ZipFile;

import java.util.zip.ZipOutputStream;

import org.apache.commons.io.IOUtils;

import com.ibatis.common.logging.Log;

import com.ibatis.common.logging.LogFactory;

public class ZipUtil {

private static final Log log = LogFactory.getLog(ZipUtil.class);

/**

* 压缩文件

*

* @param srcfile File[] 需要压缩的文件列表

* @param zipfile File 压缩后的文件

*/

public static OutputStream zipFiles(List srcfile, OutputStream outputStream) {

byte[] buf = new byte[1024];

try {

// Create the ZIP file

ZipOutputStream out = new ZipOutputStream(outputStream);

// Compress the files

for (int i = 0; i < srcfile.size(); i++) {

File file = srcfile.get(i);

FileInputStream in = new FileInputStream(file);

// Add ZIP entry to output stream.

out.putNextEntry(new ZipEntry(file.getName()));

// Transfer bytes from the file to the ZIP file

int len;

while ((len = in.read(buf)) > 0) {

//System.out.println(len+"==============");

out.write(buf, 0, len);

}

// Complete the entry

out.closeEntry();

in.close();

}

// Complete the ZIP file

out.close();

} catch (IOException e) {

log.error("ZipUtil zipFiles exception:"+e);

}

return outputStream;

}

/**

* 压缩文件

*

* @param srcfile File[] 需要压缩的文件列表

* @param zipfile File 压缩后的文件

*/

public static void zipFiles(List srcfile, File zipfile) {

byte[] buf = new byte[1024];

try {

// Create the ZIP file

ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipfile));

// Compress the files

for (int i = 0; i < srcfile.size(); i++) {

File file = srcfile.get(i);

FileInputStream in = new FileInputStream(file);

// Add ZIP entry to output stream.

out.putNextEntry(new ZipEntry(file.getName()));

// Transfer bytes from the file to the ZIP file

int len;

while ((len = in.read(buf)) > 0) {

out.write(buf, 0, len);

}

// Complete the entry

out.closeEntry();

in.close();

}

// Complete the ZIP file

out.close();

} catch (IOException e) {

log.error("ZipUtil zipFiles exception:"+e);

}

}

/**

* 压缩文件

* srcfile:key:文件名,value:文件对应的输入流

* @param srcfile

* @param zipfile

* @see

*/

public static void zipByStream(Map srcfile, File zipfile) {

try {

// Create the ZIP file

ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipfile));

// Compress the files

System.out.println(srcfile.entrySet().size());

for (Map.Entry fileEntry:srcfile.entrySet()) {

InputStream in = fileEntry.getValue();

// Add ZIP entry to output stream.

System.out.println(in.available());

out.putNextEntry(new ZipEntry(fileEntry.getKey()));

// Transfer bytes from the file to the ZIP file

byte[] bytes=IOUtils.toByteArray(in);

out.write(bytes);

out.closeEntry();

in.close();

}

// Complete the ZIP file

out.close();

} catch (IOException e) {

log.error("ZipUtil zipFiles exception:"+e);

System.out.println(e.getMessage());

}

}

/**

* 压缩文件

* srcfile:key:文件名,value:文件对应的字节数组

* @param srcfile

* @param zipfile

* @see

*/

public static void zipByArray(Map srcfile, File zipfile) {

byte[] buf = new byte[1024];

try {

// Create the ZIP file

ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipfile));

// Compress the files

System.out.println(srcfile.entrySet().size());

for (Map.Entry fileEntry:srcfile.entrySet()) {

//InputStream in = fileEntry.getValue();

// Add ZIP entry to output stream.

out.putNextEntry(new ZipEntry(fileEntry.getKey()));

// Transfer bytes from the file to the ZIP file

byte[] bytes=fileEntry.getValue();//IOUtils.toByteArray(in);

out.write(bytes);

out.closeEntry();

//in.close();

}

// Complete the ZIP file

out.close();

} catch (IOException e) {

log.error("ZipUtil zipFiles exception:"+e);

System.out.println(e.getMessage());

}

}

/**

* 解压缩

*

* @param zipfile File 需要解压缩的文件

* @param descDir String 解压后的目标目录

*/

public static void unZipFiles(File zipfile, String descDir) {

try {

// Open the ZIP file

ZipFile zf = new ZipFile(zipfile);

for (Enumeration entries = zf.entries(); entries.hasMoreElements();) {

// Get the entry name

ZipEntry entry = ((ZipEntry) entries.nextElement());

String zipEntryName = entry.getName();

InputStream in = zf.getInputStream(entry);

// System.out.println(zipEntryName);

OutputStream out = new FileOutputStream(descDir + zipEntryName);

byte[] buf1 = new byte[1024];

int len;

while ((len = in.read(buf1)) > 0) {

out.write(buf1, 0, len);

}

// Close the file and stream

in.close();

out.close();

}

} catch (IOException e) {

log.error("ZipUtil unZipFiles exception:"+e);

}

}

/**

* Main

*

* @param args

*/

public static void main(String[] args) {

List srcfile=new ArrayList();

srcfile.add(new File("d:\\1.jpg"));

srcfile.add(new File("d:\\2.jpg"));

srcfile.add(new File("d:\\3.jpg"));

srcfile.add(new File("d:\\4.jpg"));

File zipfile = new File("d:\\pic.zip");

ZipUtil.zipFiles(srcfile, zipfile);

}

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

java 多线程下载图片_java多线程实现下载图片并压缩相关推荐

  1. java 多线程下载器_Java多线程的下载器(1)

    实现了一个基于Java多线程的下载器,可提供的功能有: 1. 对文件使用多线程下载,并显示每时刻的下载速度. 2. 对多个下载进行管理,包括线程调度,内存管理等. 一:单个文件下载的管理 1. 单文件 ...

  2. java 多线程下载 断点_Java 多线程断点下载文件

    基起原根蒂根基理:哄骗URLConnection获取要下载文件的长度.头部等相干信息,并设置响应的头部信息.并且经由过程URLConnection获取输入流,将文件分成指定的块,每一块零丁开辟一个线程 ...

  3. java线程代码实现_Java 多线程代码实现讲解

    作为一个完全面向对象的语言,Java提供了类 java.lang.Thread 来方便多线程编程,这个类提供了大量的方法来方便我们控制自己的各个线程.那么如何提供给 Java 我们要线程执行的代码呢? ...

  4. java 锁旗标_Java多线程

    Java多线程 1. 多线程存在的意义 多线程最大的作用就是能够创建"灵活响应"的桌面程序,而编写多线程最大的困难就是不同线程之间共享资源的问题,要使这些资源不会同时被多个线程访问 ...

  5. java线程怎么用_Java多线程基本使用

    一.概念 1.进程 1.1进程:是一个正在进行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元. 1.2线程:就是进程中一个独立的控制单元,线程在控制着进程的执行,一 ...

  6. java html 转图片_Java 将Excel转为图片、html、XPS、XML、CSV

    通过文档格式转换,可满足不同办公场合对文档操作的需求.本文将介绍转换Excel文档为其他常见文档格式的方法.通过文中的方法,可支持将Excel转换为包括PDF.图片.html.XPS.XML.CSV. ...

  7. java上传网络图片_java网络编程之图片上传

    输入输出流核心代码 所有的文件传输都是靠流,其中文件复制最具代表性.输入流和输出流,从输入流中读取数据写入到输出流中. InputStream in =输入源; OutputStream os=输出目 ...

  8. java添加背景图片_Java怎么添加背景图片

    首先,导入相关的包: import java.awt.BorderLayout; import java.awt.Container; import javax.swing.ImageIcon; im ...

  9. java 把文字转成图片_java文本文件转化为图片文件怎么弄?

    展开全部 文件在计算机中都是以二62616964757a686964616fe58685e5aeb931333335343365进制保存的,但系统是以文件头来区分各种文件格式的. 也就是说,仅仅更改后 ...

最新文章

  1. 2018java二级考试大纲_2018年全国计算机二级java考试简答题练习二
  2. Xamarin.iOS模拟器调试找不到资源文件
  3. Python入门习题9.数码管时间
  4. 群星巨型计算机事件,群星 三种特殊事件介绍 特殊事件有几种
  5. 用html5做一个简单网页_用Python做一个简单的翻译工具
  6. 【DP】晨练计划(ybtoj)
  7. android-x86 镜像iso下载_2019年微软MSDN原版镜像系统下载地址 Win10/7原版系统iso镜像文件...
  8. 我把自己的下半生用来写程序
  9. React的Element的创建和render
  10. 《解读NoSQL》——2.6 通过数据库分片获得水平扩展能力
  11. 国人项目上了Github全球热榜,之后都发生了什么?
  12. 小学计算机应用到英语课教案,人教版小学英语三年级上册unit one hello!文具单词教学信息技术应用成果(教学设计方案).doc...
  13. Spring源码系列 — BeanDefinition
  14. tapestry5 中文文档
  15. 计算机控制总线传输的是,总线,地址总线,数据总线和控制总线
  16. 红米 android8 刷机,【红米6 安卓8.1线刷包】MIUI V9.6.7.0.OCGCNFD稳定版 线刷精简包...
  17. java实现 洛谷 P1427 小鱼的数字游戏
  18. JAVA Signal Handing
  19. CAD如何附着外部参照图?
  20. http各个状态码的含义:

热门文章

  1. StarDict On MAC
  2. Android - 震动反馈和提示音实现
  3. 开发,测试和开发测试工程师的区别
  4. 实现“提示信息”中英文的切换
  5. wxWidget入门(八)
  6. accomplish、complete、finish、achieve和fulfill
  7. 鏖战双11,电商架构大起底
  8. [机械臂笔记001]论文阅读-StructDiffusion: Object-Centric Diffusion for Semantic Rearrangement of Novel Objects
  9. mpu6050中文数据手册
  10. 用友科技软件测试,用友软件测试题_.doc