java多線程實(shí)現(xiàn)下載圖片并壓縮
最近在做一個(gè)需求:從其他系統(tǒng)的ftp目錄下載存儲(chǔ)圖片url的文件,然后讀取文件中的url地址,根據(jù)地址下載圖片后按天壓縮成一個(gè)包,平均一個(gè)地址文件中包含4000個(gè)地址左右,也就是說一個(gè)文件掃描后需要下載4000個(gè)左右的圖片,然后壓縮,下面把我的實(shí)現(xiàn)方式和優(yōu)化過程記錄下來,如果大家有什么更好的方式可以分享。
使用框架:SpringMVC
定時(shí)任務(wù)實(shí)現(xiàn):繼承org.springframework.scheduling.quartz.QuartzJobBean;
ftp環(huán)境搭建就不說了,在其他博客記錄過,使用虛擬機(jī)中的CentOS搭建的FTP服務(wù),創(chuàng)建FTP賬號及對應(yīng)目錄,事先上傳需要下載的圖片地址文件。文件內(nèi)容格式“圖片ID||圖片地址”。
方法一、最簡單的實(shí)現(xiàn)方法就是先下載存儲(chǔ)圖片url地址的文件,然后讀取文件遍歷圖片地址,調(diào)下載圖片的方法將圖片存儲(chǔ)到本地,最后壓縮下載的圖片,完成后刪除下載的圖片,只保留壓縮包。
public class PictureTransferJob extends QuartzJobBean
{
protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException
{
//實(shí)際的FTP配置是讀取配置文件獲取的
//FTP地址
String hostName ="192.168.1.112";
//FTP端口
int port = 2001;
/FTP賬號
String userName = "test1";
//ftp密碼
String password = "test1";
//ftp文件存儲(chǔ)目錄
String ftpDowload = "/";
//文件本地存儲(chǔ)路徑
String path = this.getClass().getResource("/").getPath();
//圖片地址文件存儲(chǔ)目錄
String addrPath=path.substring(1, path.indexOf("WEB-INF/classes"))+"picAddr";
//實(shí)際下載的圖片存儲(chǔ)目錄
String picPath=path.substring(1, path.indexOf("WEB-INF/classes"))+"pic";
addrPath = addrPath.replace("%20"," ");
picPath = picPath.replace("%20"," ");
try
{
//創(chuàng)建存儲(chǔ)圖片地址的文件
creatFile(addrPath);
//創(chuàng)建存儲(chǔ)實(shí)際圖片的文件
creatFile(picPath);
String oldAddrPath = addrPath;
String oldPicPath = picPath;
//創(chuàng)建FTP連接
FtpUtil2 ftpUtil2 = new FtpUtil2(hostName, port,userName, password, ftpDowload, true);
//遍歷FTP目錄下的文件
String[] files = ftpUtil2.ListAllFiles();
//本地?cái)?shù)據(jù)庫會(huì)有一個(gè)表記錄下載過的文件,這里會(huì)查詢數(shù)據(jù)庫和ftp列出的文件名比較,如果已經(jīng)下載過的文件就不會(huì)下載,避免重復(fù)下載。
//下面省略比較的過程,循環(huán)files數(shù)組,在本地創(chuàng)建文件
for(int i=0;i<files.length;i++){
creatFile(addrPath+File.separator+fileName);
//ftpDowload是ftp服務(wù)器存儲(chǔ)文件的地址,addrPath是本地存儲(chǔ)文件的地址
//這里一個(gè)返回狀態(tài)判斷文件是否下載成功
boolean downloadInvestorFlag = ftpUtil2.downloadFile(ftpDowload, addrPath);
//文件下載成功后調(diào)讀取文件的方法,將需要下載的圖片地址存入容器
boolean entityState = setPictureDetail(addrPath,picPath,fileNameDate);
}
}
catch (Exception e)
{
e.printStackTrace();
//調(diào)記錄錯(cuò)誤日志的業(yè)務(wù)類用于發(fā)送下載文件出錯(cuò)的短信
}
}
//這里開始讀圖片地址
private boolean setPictureDetail(String addrPath,String picPath,String synDate)
{
System.out.println("----------進(jìn)入setPictureDetail方法-----------");
BufferedReader br = null;
try
{
br=new BufferedReader(new InputStreamReader(new FileInputStream(addrPath),"UTF-8"));
String row;
int count=0;
//map中存儲(chǔ)每行讀取到的圖片名稱和URL地址
Map<String, String> addrMap=new HashMap<String, String>();
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());
//這里調(diào)用壓縮方法,壓縮方法中會(huì)調(diào)用執(zhí)行下載圖片的方法
zipPic(picPath,synDate,addrMap);
System.out.println(new Date());
System.out.println("----------完成--------------");
return true;
}
catch (Exception e)
{
e.printStackTrace();
//調(diào)用記錄錯(cuò)誤日志的業(yè)務(wù)類
return false;
}finally {
try {
if (null != br)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 根據(jù)url地址下載圖片
* @throws IOException
*/
private boolean downPic(String picPath,List<Entry<String, String>> addrList,List<File> 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<String, String> addrEntry:addrList)
{
fileName=addrEntry.getKey();
picAddr=addrEntry.getValue();
//創(chuàng)建Url對象
url=new URL(picAddr);
is=url.openStream();
//URLConnection獲取到的流通過InputStream直接寫入字節(jié)數(shù)組會(huì)缺失數(shù)據(jù),導(dǎo)致下載的圖片不完整,使用org.apache.commons.io.IOUtils.toByteArray(urlconnection.openstream())可以解決
byte[] bytes=IOUtils.toByteArray(is);//new byte[is.available()];獲取的字節(jié)
//流中數(shù)據(jù)讀入字節(jié)數(shù)組,讀入后,流中數(shù)據(jù)清空
pic=new File(picPath+fileName+".jpg");
fos=new FileOutputStream(pic);
fos.write(bytes);
//將下載的圖片存入List,待圖片全部下載完成后傳入zip方法進(jìn)行壓縮
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"));
}
/**
* 創(chuàng)建文件
* @param path
*/
private void creatFile(String path)
{
File file = new File(path);
if(!file.exists())
{
file.mkdirs();
}
}
}
方法二、多線程下載、直接壓縮流
方法一雖然實(shí)現(xiàn)了基本功能,但是由于需要下載的圖片太多,以及壓縮本地圖片文件和刪除圖片也比較耗時(shí),所以可以優(yōu)化速度的地方有兩個(gè)。一個(gè)就是提高下載圖片的效率,一個(gè)就是提高壓縮的效率。
提高下載效率的方法可以使用多線程下載,提高壓縮效率的方法是可以不將圖片保存到本地而直接壓縮文件流。
多線程實(shí)現(xiàn)方式:首先我們保存了需要下載的文件的地址列表,我們要使用多線程下載就要保證不同線程下載的圖片不會(huì)重復(fù),因此需要一個(gè)標(biāo)志來區(qū)分,這時(shí)就可以使用一個(gè)索引計(jì)數(shù)器,按每個(gè)線程下載一定量圖片分割,從0開始,每隔比如400個(gè)圖片就用一個(gè)線程下載,這樣就可以確定需要的線程個(gè)數(shù),并且每個(gè)線程下載的圖片不會(huì)重復(fù)。
壓縮文件實(shí)現(xiàn)方式:因?yàn)樯蓧嚎s文件的本質(zhì)也是讀取需要壓縮的文件流,然后生成壓縮包,因此我們可以不創(chuàng)建下載的圖片文件,而直接使用容器存儲(chǔ)所有線程下載的圖片流數(shù)據(jù),然后將流數(shù)據(jù)傳給壓縮工具類直接壓縮,這樣就省略了讀取圖片文件創(chuàng)建流,然后生成壓縮包,再刪除本地圖片文件的繁瑣過程。
下面列出改造的主要實(shí)現(xiàn):
/**
* 將下載的圖片按天壓縮
* @throws IOException
*/
private boolean zipPic(String picPath,String synDate,Map<String, String> addrMap) throws IOException{
//這里由于是多線程存儲(chǔ)圖片流,所以需要使用線程安全的map,因此使用ConcurrentHashMap
Map<String,InputStream> pictureList=new ConcurrentHashMap<String,InputStream>();
//這里定義每個(gè)線程下載的圖片個(gè)數(shù)
int count=400;
//存儲(chǔ)需要下載的圖片地址
List<Entry<String, String>> addrList=new ArrayList<Entry<String, String>>(addrMap.entrySet());
//線程數(shù),加一是因?yàn)橐獎(jiǎng)?chuàng)建一個(gè)線程下載最后不足400個(gè)的圖片
int nThreads=(addrList.size()/count)+1;
//CountDownLatch countDownLatch = new CountDownLatch(nThreads);
try
{
boolean downPic=false;
//執(zhí)行多線程下載圖片
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;
}
}
下面是創(chuàng)建線程池
/**
* 根據(jù)url地址下載圖片
* @throws InterruptedException
*/
private boolean downPic(String picPath,List<Entry<String, String>> addrList,Map<String, byte[]> picList,Map<String, InputStream> pictureList,int nThreads,int count)throws IOException, InterruptedException{
ExecutorService threadPool=Executors.newFixedThreadPool(nThreads);
// 創(chuàng)建兩個(gè)個(gè)計(jì)數(shù)器
CountDownLatch begin=new CountDownLatch(0);
CountDownLatch end=new CountDownLatch(nThreads);
// 循環(huán)創(chuàng)建線程
for (int i = 0; i < nThreads; i++) {
List<Entry<String, String>>subAddrList=null;
// 計(jì)算每個(gè)線程執(zhí)行的數(shù)據(jù)
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);
// 這里執(zhí)行線程的方式是調(diào)用線程池里的threadPool.execute(mythead)方法。
try
{
threadPool.execute(mythead);
}
catch (Exception e)
{
//記錄錯(cuò)誤日志
return false;
}
}
begin.countDown();
end.await();
// 執(zhí)行完關(guān)閉線程池
threadPool.shutdown();
//這里一定要循環(huán)直到線程池中所有線程都結(jié)束才能往下走,測試時(shí)由于沒有這一步導(dǎo)致子線程下載圖片還沒完成,而主線程已經(jīng)往下走了,導(dǎo)致壓縮包內(nèi)沒有圖片
//也可以使用CountDownLatch實(shí)現(xiàn)
/*while (true)
{
if (threadPool.isTerminated())
{
System.out.println("所有子線程已結(jié)束!");
break;
}
}*/
return true;
}
下面是線程實(shí)現(xiàn)
class PicDownload implements Runnable{
//下載圖片的地址列表
List<Entry<String, String>> addrList;
//裝載下載成功的圖片列表
Map<String, byte[]> picList;
Map<String, InputStream> pictureList;
//圖片本地存儲(chǔ)路徑
String picPath;
CountDownLatch begin,end;
public PicDownload(String picPath,List<Entry<String, String>> addrList,Map<String, InputStream> 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<Entry<String, String>> 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<String, String> addrEntry:addrList)
{
fileName=addrEntry.getKey();
picAddr=addrEntry.getValue();
//創(chuàng)建Url對象
url=new URL(picAddr);
is=url.openStream();
//URLConnection獲取到的流通過InputStream直接寫入字節(jié)數(shù)組會(huì)缺失數(shù)據(jù),導(dǎo)致下載的圖片不完整,使用org.apache.commons.io.IOUtils.toByteArray(urlconnection.openstream())可以解決
//byte[] bytes=IOUtils.toByteArray(is);//new byte[is.available()];獲取的字節(jié)
//流中數(shù)據(jù)讀入字節(jié)數(shù)組,讀入后,流中數(shù)據(jù)清空
picList.put(fileName+".jpg", is);
//這時(shí)候由于沒有把流寫入文件,一定不能關(guān)閉流,否則流中的數(shù)據(jù)就會(huì)丟失
//is.close();
}
return true;
}
catch (Exception e)
{
e.printStackTrace();
return false;
}
finally{
//不能關(guān)閉流
/*if (null!=is)
{
is.close();
}*/
}
}
}
上面使用流來壓縮遇到了另一個(gè)問題,在壓縮文件時(shí)會(huì)出現(xiàn)java.net.SocketException:Connection reset
分析了一下原因,應(yīng)該是由于流InputStream和UrlConnection是連接狀態(tài)的,UrlConnection超時(shí)重置導(dǎo)致了獲取輸入流失敗。
嘗試設(shè)置URLConnection的超時(shí)時(shí)間,但是測試時(shí)發(fā)現(xiàn)圖片下載收到網(wǎng)速影響較大,這種方式很不穩(wěn)定,不可取,最后只有放棄使用流,而改用字節(jié)數(shù)組傳給壓縮工具類,然后將字節(jié)數(shù)組轉(zhuǎn)為流壓縮。
/**
*使用容器存儲(chǔ)下載的圖片字節(jié)數(shù)組
*/
public boolean downPicture(List<Entry<String, String>> 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<String, String> addrEntry:addrList)
{
fileName=addrEntry.getKey();
picAddr=addrEntry.getValue();
//創(chuàng)建Url對象
url=new URL(picAddr);
//打開連接,創(chuàng)建java.net.URLConnection對象,該對象沒有關(guān)閉連接的方法,可以轉(zhuǎn)為它的子類HttpURLConnection調(diào)用disconnect方法關(guān)閉連接。
//java.net.URLConnection和java.net.HttpURLConnection都有設(shè)置超時(shí)時(shí)間的方法關(guān)閉連接
//HttpURLConnection uc=(HttpURLConnection)url.openConnection();
is=uc.getInputStream();
//URLConnection獲取到的流通過InputStream直接寫入字節(jié)數(shù)組會(huì)缺失數(shù)據(jù),導(dǎo)致下載的圖片不完整,使用org.apache.commons.io.IOUtils.toByteArray(urlconnection.openstream())可以解決
byte[] bytes=IOUtils.toByteArray(is);//new byte[is.available()];獲取的字節(jié)
//流中數(shù)據(jù)讀入字節(jié)數(shù)組,讀入后,流中數(shù)據(jù)清空
//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();
}
}
}
總結(jié):
實(shí)現(xiàn)過程中遇到的問題:
1、使用線程池時(shí)對于共享狀態(tài),比如這里的存儲(chǔ)下載的圖片字節(jié)數(shù)據(jù)容器是所有線程共享的,因此需要使用同步的容器,否則會(huì)導(dǎo)致存儲(chǔ)的數(shù)據(jù)出問題,因此使用了ConcurrentHashMap<String,byte[]>
2、這里存在一個(gè)主線程和子線程的執(zhí)行順序問題,因?yàn)橹骶€程需要等待線程池中所有線程下載圖片結(jié)束后才能往下走去壓縮圖片,如果主線程不等待子線程結(jié)束就向下執(zhí)行壓縮方法就會(huì)導(dǎo)致壓縮圖片缺少或者沒有壓縮圖片。因此可以使用CountDownLatch實(shí)現(xiàn),或者在關(guān)閉線程池語句下面使用死循環(huán)檢查threadPool.isTerminated()才能繼續(xù)執(zhí)行主線程去壓縮圖片。
3、由于直接將UrlConnection獲取到的輸入流直接傳給壓縮類進(jìn)行壓縮,存在連接超時(shí)重置的情況,因此改用將下載的流存入字節(jié)數(shù)組,再傳給壓縮類壓縮,避免使用流出現(xiàn)意外情況。
4、在使用urlconnection.openStream()獲取輸入流后,轉(zhuǎn)換為字節(jié)數(shù)組下載的圖片是不完整的。。使用org.apache.commons.io.IOUtils.toByteArray(urlconnection.openstream())可以解決,具體可以閱讀其源碼看實(shí)現(xiàn)。
下面是FTP工具類的實(shí)現(xiàn):
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服務(wù)器地址
private String hostName;
// ftp服務(wù)器默認(rèn)端口
public static int defaultport = 21;
// 登錄名
private String userName;
// 登錄密碼
private String password;
// 需要訪問的遠(yuǎn)程目錄
private String remoteDir;
/**
* @param hostName
* 主機(jī)地址
* @param port
* 端口號
* @param userName
* 用戶名
* @param password
* 密碼
* @param remoteDir
* 默認(rèn)工作目錄
* @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服務(wù)器
*/
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服務(wù)器:" + 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();
}
}
/**
* 列出當(dāng)前工作目錄下所有文件
*/
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<remoteFiles.length;i++) { if(remoteFiles[i] == null)
* 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();
}
}
/**
* 設(shè)置傳輸文件的類型[文本文件或者二進(jìn)制文件]
*
* @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();// 被動(dòng)模式連接
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();// 被動(dòng)模式連接
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();
}
}
}
/**
* 下載文件(單個(gè))
*
* @param remoteFileName
* --服務(wù)器上的文件名
* @param localFileName
* --本地文件名
*/
public boolean downloadFile(String remoteFileName, String localFileName) {
this.ftpClient.enterLocalPassiveMode();// 被動(dòng)模式連接
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;
}
/**
* 關(guān)閉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<File> 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<File> 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:文件對應(yīng)的輸入流
* @param srcfile
* @param zipfile
* @see
*/
public static void zipByStream(Map<String,InputStream> 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<String, InputStream> 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:文件對應(yīng)的字節(jié)數(shù)組
* @param srcfile
* @param zipfile
* @see
*/
public static void zipByArray(Map<String,byte[]> 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<String, byte[]> 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 解壓后的目標(biāo)目錄
*/
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<File> srcfile=new ArrayList<File>();
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);
}
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
java基于Des對稱加密算法實(shí)現(xiàn)的加密與解密功能詳解
這篇文章主要介紹了java基于Des對稱加密算法實(shí)現(xiàn)的加密與解密功能,結(jié)合實(shí)例形式詳細(xì)分析了Des加密算法的功能、原理、使用方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-01-01
Java8新特性之重復(fù)注解(repeating annotations)淺析
這篇文章主要介紹了Java8新特性之重復(fù)注解(repeating annotations)淺析,這個(gè)新特性只是修改了程序的可讀性,是比較小的一個(gè)改動(dòng),需要的朋友可以參考下2014-06-06
IDEA如何一鍵部署SpringBoot項(xiàng)目到服務(wù)器
文章介紹了如何在IDEA中部署SpringBoot項(xiàng)目到服務(wù)器,使用AlibabaCloudToolkit插件進(jìn)行配置部署,步驟包括設(shè)置服務(wù)名稱、選擇文件上傳類型、選擇jar文件、添加服務(wù)器信息、輸入上傳路徑、選擇上傳后執(zhí)行的腳本以及執(zhí)行前的操作命令2024-12-12
MyBatis使用注解開發(fā)和無主配置文件開發(fā)的情況
這篇文章主要介紹了MyBatis使用注解開發(fā)和無主配置文件開發(fā)的情況,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
Java Set集合及其子類HashSet與LinkedHashSet詳解
這篇文章主要介紹了Java Set集合及其子類HashSet與LinkedHashSet詳解,文章通過Set集合存儲(chǔ)原理展開文章主題相關(guān)介紹,感興趣的小伙伴可以參考一下2022-06-06
基于@AllArgsConstructor與@Value共用的問題解決
這篇文章主要介紹了基于@AllArgsConstructor與@Value共用的問題解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
java注解實(shí)現(xiàn)websocket服務(wù)的兩種方式
Java WebSocket是一種基于TCP協(xié)議的雙向全雙工消息傳輸技術(shù),它允許服務(wù)器和客戶端之間實(shí)時(shí)通信,具有低延遲和高效率的特點(diǎn),下面這篇文章主要給大家介紹了關(guān)于java注解實(shí)現(xiàn)websocket服務(wù)的兩種方式,需要的朋友可以參考下2024-08-08
SpringBoot+RabbitMQ方式收發(fā)消息的實(shí)現(xiàn)示例
這篇文章主要介紹了SpringBoot+RabbitMQ方式收發(fā)消息的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

