1. 注意事項
(1). 需求:
1.目前處理的項目中,有個任務線程會一直在網絡上下載文件,所以需要定期將這些文件打包壓縮,並且定期將過時太久的壓縮文件刪除,以防磁盤滿。
2.刪除壓縮文件時,希望是根據文件容量大小,即設置一個閾值,當文件容量大小大於這個閾值,就刪除文件,直至文件容量總量小於這個閾值。
3.刪除壓縮文件時,總是刪除最老最舊的文件,以保持存在的總是最近最新的文件。
以上三個需求除了處理像我遇到的下載文件之外,同樣可用於處理日誌文件。
(2).首先要解決的是執行這樣的定時任務。定期執行一個任務,可以使用輕量級定時調度工具cron4j,它的mvn引用如下:
<!-- https://mvnrepository.com/artifact/it.sauronsoftware.cron4j/cron4j -->
<dependency>
<groupId>it.sauronsoftware.cron4j</groupId>
<artifactId>cron4j</artifactId>
<version>2.2.5</version>
</dependency>
(3).將打包和刪除操作設置成守護線程在後臺運行,當mainthread結束退出後,守護線程也會隨之退出。
(4).生成壓縮文件時,文件名可加入時間信息以防止命名衝突。如果文件夾下還有子文件夾,需要對該文件夾的每個文件進行判斷,如果是文件,則直接加入打包,如果是目錄,則需要遞歸進行處理。本示例中沒有對這種情況進行處理,對文件夾進行遞歸壓縮的示例將在下一章“java 將指定文件夾遞歸的進行zip打包壓縮”。
(5). 在刪除文件時,一定要注意文件是否被其他進程佔用或者是否有該文件上的流沒有關閉,這個非常重要,因爲我就是因爲流沒有關閉,導致文件最後無法正常刪除。這種時候要避免使用匿名類,因爲很可能就會忽略文件上的一些流沒有關閉,導致刪除文件這個操作失敗。我會在後面的代碼示例中重點強調出來。
(6).本示例代碼只處理指定文件夾下全是文件,沒有子文件夾的情況,如果你測試本代碼時報了“(拒絕訪問。)”的錯誤,請檢查一下目錄下面是否有子文件夾。
(7).刪除文件時,由於要刪除最舊最老的文件,此時就需要對文件按命名日期進行排序,而且是倒序:因爲我實現時使用的List,刪除最後一個文件的操作的時間複雜度是O(1)。
(8).隔A時間就執行1次打包操作,隔B時間就執行1次刪除壓縮包操作,壓縮文件容量超過C字節就執行刪除,這3個值根據需要自行設置成合適的值。
2. 代碼示例:
package tmp.MavenTest;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
//定時將指定文件夾下的所有文件壓縮
public class ZipFilesUtil {
private static Logger log = Logger.getLogger("zipfiles");
//將指定文件夾下的所有文件(除壓縮文件除外)壓縮,該文件夾下沒有子文件夾,否則需遞歸進行處理
//壓縮文件名爲日期名
public static void zipFiles(String sourceFilePath){
//判斷是否有指定文件夾
File sourceFile = new File(sourceFilePath);
if(!sourceFile.exists())
{
//tmpFile.mkdirs();
log.info("待壓縮的文件目錄:"+sourceFile+"不存在");
return;
}
String zipName = generateId();//生產壓縮文件名
File zipFile = new File(sourceFile+"\\"+zipName+".zip");
File[] sourceFiles = sourceFile.listFiles();
if(null == sourceFiles || sourceFiles.length<1){
log.info("待壓縮的文件目錄:" + sourceFilePath + "裏面不存在文件,無需壓縮.");
return;
}
BufferedInputStream bis = null;
ZipOutputStream zos = null;
byte[] bufs = new byte[1024*10];
FileInputStream fis = null;
try {
zos = new ZipOutputStream(new FileOutputStream(zipFile));
for(int i=0; i<sourceFiles.length; i++){
//創建zip實體,並添加進壓縮包
String tmpFileName = sourceFiles[i].getName();
if(tmpFileName.endsWith(".zip"))
continue;
ZipEntry zipEntry = new ZipEntry(tmpFileName);
zos.putNextEntry(zipEntry);
//讀取待壓縮的文件並寫進壓縮包裏
fis = new FileInputStream(sourceFiles[i]);
bis = new BufferedInputStream(fis, 1024*10);
int read = 0;
while((read=bis.read(bufs, 0, 1024*10))!=-1){
zos.write(bufs, 0, read);
}
fis.close();//很重要,否則刪除不掉!
sourceFiles[i].delete();//將要進行壓縮的源文件刪除
}//end for
log.info("文件打包成功!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
//關閉流
try {
if(null!=bis)
bis.close();
if(null!=zos)
//zos.closeEntry();
zos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//刪除指定文件夾下的壓縮文件
public static void deleteZipFiles(String filePath) throws ParseException{
File delFile = new File(filePath);
if(!delFile.exists())
{
//tmpFile.mkdirs();
log.info("待刪除的文件目錄:"+delFile+"不存在");
return;
}
File[] delFiles = delFile.listFiles();
if(null == delFiles || delFiles.length<1){
log.info(filePath+"下沒有要刪除的文件.");
return;
}
//收集壓縮文件,過濾掉非壓縮的文件以及文件夾
List<File> delFilesTarget = new ArrayList<File>();
for(int i=0; i<delFiles.length; i++){
String tmpFileName = delFiles[i].getName();
if(tmpFileName.endsWith(".zip"))//是壓縮文件
delFilesTarget.add(delFiles[i]);
}
orderByNameDate(delFilesTarget);//按文件名的日期排序(倒序)
//計算文件大小總量,然後檢查總量是否超過閾值(100KB)。
//如果超過,則不斷刪除最老的文件,直至文件總量不再超過閾值
long len = 0;
for(int i=0; i<delFilesTarget.size(); i++){
len += delFilesTarget.get(i).length();//返回字節數
}
int threshold = 100000;//100KB,閾值
int lastIndex = delFilesTarget.size()-1;
while(len>threshold){
File delF = delFilesTarget.remove(lastIndex);
len -= delF.length();
lastIndex -= 1;
if(!delF.delete()){
log.info("文件"+delF.getName()+"刪除失敗!");
}else{
log.info("文件"+delF.getName()+"刪除成功!");
}
}
}
//以日期生成文件名
public static String generateId(){
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");//設置日期格式
String dateStr = df.format(new Date());//new Date()爲獲取當前系統時間
return dateStr;
}
//按文件名的日期排序(倒序)
public static void orderByNameDate(List<File> files){
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
Collections.sort(files, new Comparator<File>() {
public int compare(File f1, File f2) {
// TODO Auto-generated method stub
try {
Date f1Date = sdf.parse(f1.getName());
Date f2Date = sdf.parse(f2.getName());
//return f1Date.compareTo(f2Date);//正序
return f2Date.compareTo(f1Date);//逆序
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return 0;
}
});
}
}
package tmp.MavenTest;
import java.text.ParseException;
import it.sauronsoftware.cron4j.Scheduler;
public class Test
{
private static Scheduler zipScheduler = null;
private static Scheduler delScheduler = null;
public static void main( String[] args )
{
System.out.println( "Main thread start..." );
String filePath = "D:\\Eclipse_XJ\\work_space\\WebProbe\\downloadfiles";
startZipAndDelFilesJop(filePath);//啓動定期打包任務和定期刪除任務
try{
Thread.sleep(60*60*1000);//調度持續1小時,即mainthread 會在這裏阻塞(睡眠)1小時
} catch(InterruptedException e){
e.printStackTrace();
}
//zipScheduler.stop();//如果不設置成守護進程,就需要手動停止
//delScheduler.stop();
System.out.println( "Main thread end..." );
//此時守護進程也會隨之退出
}
//啓動定期打包任務和定期刪除任務
public static void startZipAndDelFilesJop(final String filePath){
zipScheduler = new Scheduler();
//每2分鐘執行1次打包job
zipScheduler.schedule("*/2 * * * *", new Runnable() {//前面五個*號按順序分別代表:分鐘、小時、日、月、星期
public void run() {
// TODO Auto-generated method stub
System.out.println( "每2分鐘執行1次打包job" );
ZipFilesUtil.zipFiles(filePath);
}
});
zipScheduler.setDaemon(true);//設置爲守護進程
zipScheduler.start();
delScheduler = new Scheduler();
//每3分鐘執行1次刪除job
delScheduler.schedule("*/3 * * * *", new Runnable() {//前面五個*號按順序分別代表:分鐘、小時、日、月、星期
public void run() {
// TODO Auto-generated method stub
System.out.println( "每3分鐘執行1次刪除job" );
try {
ZipFilesUtil.deleteZipFiles(filePath);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
delScheduler.setDaemon(true);//設置爲守護進程
delScheduler.start();
}
}