java基于servlet實現(xiàn)文件上傳功能解析
最近項目需要做一個文件上傳功能,做完了分享下,順帶當做筆記。
上傳功能用后臺用java實現(xiàn),前端主要是js的ajax實現(xiàn)。后臺還加入定時刪除臨時文件。
效果如圖


首先是上傳功能的主要類,下面是代碼
package util.upload;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadServlet extends HttpServlet {
private static final long serialVersionUID = -3100028422371321159L;
private boolean isAllowed;
private String upFileName;
//定義合法后綴名的數(shù)組
private String[] allowedExtName=new String[]
{"zip","rar",//壓縮文件
"txt","doc","wps","docx","java",//文本
"xls","xlsx",//表格
"ppt","pptx",//幻燈片
"pdf",//pdf
"jpg","jpeg","bmp","gif","png"http://圖片
};
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//設置編碼格式為utf-8
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
//獲取session,保存進度和上傳結果,上傳開始為nok,當為Ok表示上傳完成
HttpSession session=request.getSession();
session.setAttribute("result", "nok");
session.setAttribute("error", "");
String error="";
upFileName="";
isAllowed=false;
//給上傳的文件設一個最大值,這里是不得超過100MB
int maxSize=100*1024*1024;
//創(chuàng)建工廠對象和文件上傳對象
DiskFileItemFactory factory=new DiskFileItemFactory();
ServletFileUpload upload=new ServletFileUpload(factory);
//創(chuàng)建上傳監(jiān)聽器和設置監(jiān)聽器
UploadListener listener=new UploadListener();
session.setAttribute("LISTENER", listener);
upload.setProgressListener(listener);
//上傳路徑
String path = request.getSession().getServletContext().getRealPath("/upload");
String requestPath = request.getSession().getServletContext().getContextPath()+"/upload";
File dirFile =new File(path); //System.out.println(request.getSession().getServletContext().getContextPath());
//如果文件夾不存在則創(chuàng)建
if (!dirFile .exists() && !dirFile .isDirectory())
{
dirFile .mkdir();
}
//根據(jù)日期創(chuàng)建文件夾,保存到對應日期的文件夾下
Date date=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMdd");
String subDirName=sdf.format(date);
File subDirFile=new File(path+"/"+subDirName);
if (!subDirFile .exists() && !subDirFile .isDirectory())
{
subDirFile .mkdir();
}
try {
//解析上傳請求
List<FileItem> items=upload.parseRequest(request);
Iterator<FileItem> itr=items.iterator();
while(itr.hasNext()){
FileItem item=(FileItem)itr.next();
//判斷是否為文件域
if(!item.isFormField()){ if(item.getName()!=null&&!item.getName().equals("")){
//獲取上傳文件大小和文件名稱
long upFileSize=item.getSize();
String fileName=item.getName();
//獲取文件后綴名
String[] splitName=fileName.split("\\.");
String extName=splitName[splitName.length-1];
//檢查文件后綴名
for(String allowed:allowedExtName)
{
if(allowed.equalsIgnoreCase(extName))
{
isAllowed=true;
}
}
if(!isAllowed){
error="上傳文件格式不合法!";
break;
}
if(upFileSize>maxSize){
error="您上傳的文件太大了,請選擇不超過100MB的文件!";
break;
}
//此時文件暫存在服務器的內存中,構造臨時對象
File tempFile=new File(makeFileName(fileName));
//指定文件上傳服務器的目錄及文件名稱
File file=new File(path+"/"+subDirName+"/",tempFile.getName());
item.write(file);//第一種寫文件方法
upFileName=requestPath+"/"+subDirName+"/"+tempFile.getName();
if(upFileName.equals("")){
error="沒選擇上傳文件!";
}
System.out.println(upFileName);
/*//構造輸入流讀文件 第二種寫文件方法
InputStream is=item.getInputStream();
int length=0;
byte[] by=new byte[1024];
FileOutputStream fos=new FileOutputStream(file);
while((length=is.read(by))!=-1){
fos.write(by, 0, length);
//Thread.sleep(10);
}
fos.close();
//Thread.sleep(1000);*/
}else{
error="沒選擇上傳文件!";
}
}
}
} catch (Exception e) {
e.printStackTrace();
error="上傳文件出現(xiàn)錯誤:"+e.getMessage();
}
if(!error.equals("")){
System.out.println(error);
session.setAttribute("error", error);
}else{
session.setAttribute("result", "OK");
session.setAttribute("filename",upFileName);
}
}
/**
* 為防止文件覆蓋的現(xiàn)象發(fā)生,要為上傳文件產生一個唯一的文件名
* @param filename 原文件名
* @return 生成的唯一文件名
*/
private String makeFileName(String filename){
return UUID.randomUUID().toString() + "_" + filename;
}
}
其中需要引入commons-fileupload-1.3.1.jar,commons-io-2.4.jar
上傳過程中,我們需要實時獲取上傳進度等信息,引入的庫里為我們添加了一個ProgressListener接口,我們再寫一個類實現(xiàn)這個接口,上面類中添加該接口
//創(chuàng)建工廠對象和文件上傳對象
DiskFileItemFactory factory=new DiskFileItemFactory();
ServletFileUpload upload=new ServletFileUpload(factory);
//創(chuàng)建上傳監(jiān)聽器和設置監(jiān)聽器
UploadListener listener=new UploadListener();
session.setAttribute("LISTENER", listener);
upload.setProgressListener(listener);
下面是這個監(jiān)聽類的具體實現(xiàn)代碼
package util.upload;
import org.apache.commons.fileupload.ProgressListener;
public class UploadListener implements ProgressListener{
private volatile long
bytesRead = 0L,//上傳的字節(jié)數(shù)
contentLength = 0L,//總字節(jié)數(shù)
item = 0L;
public UploadListener()
{
super();
}
@Override
public void update(long aBytesRead, long aContentLength, int anItem) {
bytesRead = aBytesRead;
contentLength = aContentLength;
item = anItem;
}
public long getBytesRead()
{
return bytesRead;
}
public long getContentLength()
{
return contentLength;
}
public long getItem()
{
return item;
}
}
現(xiàn)在能獲取上傳進度等信息了,但還需要一個servlet返回給前端,下面實現(xiàn)
package util.upload;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.ProgressListener;
import com.google.gson.Gson;
/**
獲取上傳進度,上傳路徑,錯誤,上傳結果等信息
*/
public class GetProgressServlet extends HttpServlet{
private static final long serialVersionUID = -3596466520775012991L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
UploadListener listener= null;
HttpSession session = request.getSession();
String error=(String) session.getAttribute("error");
String result= (String) session.getAttribute("result");
String fileName=(String) session.getAttribute("filename");
PrintWriter out = response.getWriter();
long bytesRead = 0,contentLength = 0;
if (session != null)
{
listener = (UploadListener)session.getAttribute("LISTENER");
if (listener == null)
{
return;
}
else
{
bytesRead = listener.getBytesRead();//上傳的字節(jié)數(shù)
contentLength = listener.getContentLength();//總字節(jié)數(shù)
}
//自己定義的返回格式
String rp=bytesRead+","
+contentLength+","
+error+","
+result+","
+fileName;
//System.out.println(rp);
out.print(rp);
/* //返回json格式數(shù)據(jù)
Map<String,Object> map=new HashMap<String,Object>();
map.put("bytesRead", bytesRead);
map.put("contentLength", contentLength);
map.put("error", error);
map.put("result", result);
map.put("fileName", fileName);
Gson gson=new Gson();
String json=gson.toJson(map);
out.print(json);*/
out.flush();
out.close();
}
}
}
后臺上傳的功能代碼寫完了,下面實現(xiàn)上傳的前端,首先是html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script type="text/javascript" src="js/upfile.js" charset="utf-8"></script>
<link rel="stylesheet" type="text/css" href="css/upfile.css">
</head>
<body >
<a href="javascript:addOne()">添加</a>
<div id="target">
<input type="file" id="file" name="file" onchange="addfile(event)" multiple/>
</div>
<span id="test">0</span>
</body>
</html>
界面比較簡單,就一個添加的a標簽,負責上傳的input隱藏起來
css文件主要渲染的上傳進度的顯示
#file {
display: none;
}
.pro{
width:500px;
}
.pborder {
position: relative;
width: 500px; /* 寬度 */
border: 1px solid #B1D632;
padding: 1px;
}
.drawpro {
width: 0px;
display: block;
position: relative;
background: #B1D632;
color: #333333;
height: 20px; /* 高度 */
line-height: 20px; /* 必須和高度一致,文本才能垂直居中 */
}
.pspan {
position: absolute;
width: 500px;
text-align: center;
font-weight: bold;
}
接著是前端的重點,js文件
//顯示上傳信息的html
var upfile_html = '<div class="pborder"><div class="drawpro">'
+ '<span class="pspan">0%</span></div></div><span name="path"></span><img src="common/upload/images/del.png" style="float:right" width="20" height="20" name="del" onclick=abortUpload(this)>';
var targetDIV_id = "target";//顯示上傳文件的目標div的ID
var httpXML = null;//發(fā)送上傳請求的XMLHttpRequest對象
var httpProgress = null;//發(fā)送請求進度信息的XMLHttpRequest對象
var oldFileList = new Array();//修改時保存已有附件信息的列表
var uplist = new Array();//保存上傳文件的列表
var f_input;//上傳文件的input對象
var flag = true;//是否可以上傳下一個文件標志
var uurl = "Upload";//上傳文件的請求url
var gurl = "getProgress";//獲取上傳進度信息的url
var cancelFlag = 0;//取消標志
var timer, waittimer;//定時器
var nowID = 0;//正在上傳文件的id
var ID = 0;//隊列中最后一個文件的id
/**
* 文件對象
*/
function UploadFile(id, file) {
this.id = id;
this.file = file;
this.state = 0;
this.path = "";
}
/**
* 初始化的方法
*/
window.onload = function init() {
f_input = document.getElementById("file");
var tdiv = document.getElementById(targetDIV_id);
var oldspan = tdiv.getElementsByTagName("SPAN");
for ( var i = 0; i < oldspan.length; i++) {
oldFileList.push(oldspan[i].getAttribute("name"));
}
}
/**
* 選擇一個文件上傳
*/
function addOne() {
f_input.value = null;
f_input.click();
}
/**
* 選中文件后將文件對象添加到隊列,開始上傳
*
*/
function addfile(evt) {
var f = f_input.files[0];
if (f != undefined) {
var uf = new UploadFile(ID, f);
uplist.push(uf);
var div = document.createElement("DIV");
div.setAttribute("id", "pro" + (ID));
div.setAttribute("class", "pro");
div.innerHTML = upfile_html;
var targetDiv = document.getElementById(targetDIV_id);
targetDiv.appendChild(div);
div.getElementsByTagName("SPAN")[1].innerHTML = "文件名:"
+ uplist[ID].file.name;
waittimer = setInterval("upload()", 1000);
ID++;
}
}
/**
* 將隊列中的文件上傳
*/
function upload() {
if (flag == true) {
if (uplist.length > 0) {
var uf;
for ( var i = 0; i < uplist.length; i++) {
if (uplist[i].state == 0) {
uf = uplist[i];
uplist[i].state = 1;
break;
}
}
if (uf != undefined & uf != null) {
flag = false;
if (window.XMLHttpRequest) {
httpUP = new XMLHttpRequest();
} else if (window.ActiveXObject) {
httpUP = new ActiveXObject("Microsoft.XMLHTTP");
}
var formData = new FormData();
formData.append("file", uf.file);
httpUP.open("POST", uurl, true);
httpUP.upload.addEventListener('progress', uploadProgress, false);
httpUP.send(formData);
nowID = uf.id;
timer = setInterval("getP()", 50);
}
}
}
}
/**
* 獲取上傳進度等信息
*/
function getP() {
if (window.XMLHttpRequest) {
httpProgress = new XMLHttpRequest();
} else if (window.ActiveXObject) {
httpProgress = new ActiveXObject("Microsoft.XMLHTTP");
}
httpProgress.onreadystatechange = onProgress;
httpProgress.open("post", gurl, true);
httpProgress.setRequestHeader("Content-type",
"application/x-www-form-urlencoded");
httpProgress.send("&timeStamp=" + (new Date()).getTime());
}
/**
* 處理返回的上傳信息,顯示到界面
*/
function onProgress() {
if (httpProgress.readyState == 4 && httpProgress.status == 200) {
result = httpProgress.responseText;
var result = result.replace(/(^\s*)|(\s*$)/g, "");
var res = result.split(",");
var now = parseInt(res[0]);
var all = parseInt(res[1]);
var err = res[2];
var state = res[3];
var path = res[4];
var per = (now / all * 100).toFixed(2);
var prodiv = document.getElementById("pro" + nowID);
if (prodiv != null & prodiv != undefined) {
if (err != "" & err != null & err.length > 0) {
window.clearInterval(timer);
if (cancelFlag == 1) {
err = "上傳終止";
cancelFlag = 0;
}
prodiv.getElementsByTagName("DIV")[0].style.display = "none";
prodiv.getElementsByTagName("SPAN")[1].innerHTML = err;
httpUP.abort();
flag = true;
uplist[nowID].state = 3;
return;
}
if (state == "OK") {
prodiv.getElementsByTagName("DIV")[0].style.display = "none";
var tmpf = uplist[nowID].file;
prodiv.getElementsByTagName("SPAN")[1].innerHTML = "文件名:"
+ tmpf.name;
window.clearInterval(timer);
flag = true;
uplist[nowID].state = 2;
uplist[nowID].path = path;
return;
}
prodiv.getElementsByTagName("DIV")[1].style.width = per * 5 + "px";
prodiv.getElementsByTagName("SPAN")[0].innerHTML = per + "%";
}
}
}
/**
* 取消上傳的方法
*/
function abortUpload(obj) {
var idStr = obj.parentNode.id;
var id = idStr.slice(3);
if (uplist[id].state == 1) {
httpUP.abort();
flag = true;
cancelFlag = 1;
} else {
uplist[id].state = 3;
}
document.getElementById(idStr).remove();
}
/**
* 獲取上傳文件的路徑
* @returns 格式化后字符串
*/
function getFileListStr() {
var str = "";
if (oldFileList.length > 0) {
for ( var i = 0; i < oldFileList.length; i++) {
if (oldFileList[i] != null & oldFileList[i] != ""
& oldFileList[i] != undefined) {
str = str + oldFileList[i] + ",";
}
}
}
for ( var i = 0; i < uplist.length; i++) {
var f = uplist[i];
if (f.state == 2) {
str = str + f.path + ",";
}
}
return str;
}
/**
* 移除修改時已有的舊附件
*
*/
function removeOld(btn) {
var num = btn.getAttribute("name");
oldFileList[num - 1] = null;
btn.parentNode.remove();
}
function uploadProgress(e) {
if (e.lengthComputable) {
var iBytesUploaded = e.loaded;
var iBytesTotal = e.total;
document.getElementById("test").innerHTML=iBytesUploaded+"/"+iBytesTotal;
}
}
使用ajax發(fā)送上傳文件,獲取上傳進度,結果等信息。
使用的html5的file API,所以必須ie9以上的才可以兼容,火狐還有個問題,ajax請求不立即返回,直到所有ajax請求都發(fā)送完了,才都返回同一個結果,這就導致上傳進度不顯示。不過上傳進度信息也可以使用html5的file api獲取,其中加了一點代碼,頁面下面test的div里的數(shù)值就是在前端獲取到的進度。
上傳的都實現(xiàn)完了,接著是處理上傳后的臨時文件,因為使用的uuid命名文件,所以文件會生成很多,沒用的需要定時處理。使用ServletContextListener:
在 Servlet API 中有一個 ServletContextListener 接口,它能夠監(jiān)聽 ServletContext 對象的生命周期,實際上就是監(jiān)聽 Web 應用的生命周期。
當Servlet 容器啟動或終止Web 應用時,會觸發(fā)ServletContextEvent 事件,該事件由ServletContextListener 來處理。在 ServletContextListener 接口中定義了處理ServletContextEvent 事件的兩個方法。
利用其特性,實現(xiàn)定時刪除臨時文件的功能,代碼如下:
package util.upload;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* 時間監(jiān)聽器
*
*
*/
public class TempFileListener implements ServletContextListener {
private Timer timer;
private SystemTaskTest systemTask;
private static String every_time_run;
static {
Properties prop = new Properties();
InputStream inStrem = TempFileManager.class.getClassLoader()
.getResourceAsStream("tempfile.properties");
try {
prop.load(inStrem);
System.out.println(inStrem);
every_time_run = prop.getProperty("every_time_run");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inStrem.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 監(jiān)聽器初始方法
public void contextInitialized(ServletContextEvent sce) {
timer = new Timer();
systemTask = new SystemTaskTest(sce.getServletContext()
.getRealPath("/"), sce.getServletContext());
try {
System.out.println("定時器已啟動");
// 監(jiān)聽器獲取網站的根目錄
String path = sce.getServletContext().getRealPath("/");
Long time = Long.parseLong(every_time_run) * 1000;// 循環(huán)執(zhí)行的時間
System.out.println("time" + time);
// 第一個參數(shù)是要運行的代碼,第二個參數(shù)是從什么時候開始運行,第三個參數(shù)是每隔多久在運行一次。重復執(zhí)行
timer.schedule(systemTask, 10000, time);
System.out.println("已經添加任務調度表");
} catch (Exception e) {
e.printStackTrace();
}
}
public void contextDestroyed(ServletContextEvent sce) {
try {
timer.cancel();
} catch (Exception e) {
}
}
}
/**
* 時間任務器
*
*/
class SystemTaskTest extends TimerTask {
private ServletContext context;
private String path;
public SystemTaskTest(String path, ServletContext context) {
this.path = path;
this.context = context;
}
/**
* 把要定時執(zhí)行的任務就在run中
*/
public void run() {
TempFileManager etf;
try {
System.out.println("開始執(zhí)行任務!");
// 需要執(zhí)行的代碼
System.out.println(new Date().toLocaleString());
etf = new TempFileManager(path);
etf.run();
System.out.println("指定任務執(zhí)行完成!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
上面只是監(jiān)聽器,負責定時調用刪除臨時文件的方法,具體實現(xiàn)是下面的類
package util.upload;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Properties;
/**
* 刪除服務器上的文件
*
*/
public class TempFileManager implements Runnable {
private String path;//路徑
private static String RETENTION_TIME = "1440";// 文件保存的時間 一天單位分
static {
Properties prop = new Properties();
InputStream inStrem = TempFileManager.class.getClassLoader()
.getResourceAsStream("execl.properties");
try {
prop.load(inStrem);
RETENTION_TIME = prop.getProperty("file_retention_time");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inStrem.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 構造函數(shù)。初始化參數(shù)
* @param path
*/
public TempFileManager(String path) {
this.path = path;
}
/**
* 把線程要執(zhí)行的代碼放在run()中
*/
public void run() {
System.out.println("文件管理開始=========");
path = path + "upload";
System.out.println("文件管理路徑===" + path);
File file = new File(path);
deletefiles(file);
}
/**
* 批量刪除文件
*
* @param folder
*/
public void deletefiles(File folder) {
if(folder.isDirectory()){
File[] files = folder.listFiles();
if(files.length<=0){
if(!folder.getAbsolutePath().equalsIgnoreCase(path)){
if(canDeleteFile(folder)){
if (folder.delete()) {
System.out.println("文件夾" + folder.getName() + "刪除成功!");
} else {
System.out.println("文件夾" + folder.getName()
+ "刪除失敗!此文件夾內的文件可能正在被使用");
}
}
}
}
for (int i = 0; i < files.length; i++) {
if(files[i].isDirectory())
{
deletefiles(files[i]);
}else{
deleteFile(files[i]);
}
}
}
}
/**
* 刪除文件
*
* @param file
*/
private void deleteFile(File file) {
try {
if (file.isFile()) {
// 刪除符合條件的文件
if (canDeleteFile(file)) {
if (file.delete()) {
System.out.println("文件" + file.getName() + "刪除成功!");
} else {
System.out.println("文件" + file.getName()
+ "刪除失敗!此文件可能正在被使用");
}
} else {
}
} else {
System.out.println("沒有可以刪除的文件了");
}
} catch (Exception e) {
System.out.println("刪除文件失敗========");
e.printStackTrace();
}
}
/**
* 判斷文件是否能夠被刪除
*/
private boolean canDeleteFile(File file) {
Date fileDate = getfileDate(file);
Date date = new Date();
long time = (date.getTime() - fileDate.getTime()) / 1000 / 60
- Integer.parseInt(RETENTION_TIME);// 當前時間與文件間隔的分鐘
// System.out.println("time=="+time);
if (time > 0) {
return true;
} else {
return false;
}
}
/**
* 獲取文件最后的修改時間
*
* @param file
* @return
*/
private Date getfileDate(File file) {
long modifiedTime = file.lastModified();
Date d = new Date(modifiedTime);
return d;
}
}
判斷文件是否超時,超時就自動刪除,并且能自動刪除文件夾。
以上就是本文的全部內容,希望對大家學習java程序設計有所幫助。
- JavaServlet的文件上傳和下載實現(xiàn)方法
- Java Servlet簡單實例分享(文件上傳下載demo)
- java基于servlet編寫上傳下載功能 類似文件服務器
- Java Servlet上傳圖片到指定文件夾并顯示圖片
- java基于servlet的文件異步上傳
- java基于servlet實現(xiàn)文件上傳功能
- 基于java文件上傳-原始的Servlet方式
- java基于servlet使用組件smartUpload實現(xiàn)文件上傳
- java web中的servlet3 upload上傳文件實踐
- 簡單操作實現(xiàn)Java jsp servlet文件上傳過程解析
- JavaWeb servlet實現(xiàn)下載與上傳功能的方法詳解
相關文章
java動態(tài)添加外部jar包到classpath的實例詳解
這篇文章主要介紹了java動態(tài)添加外部jar包到classpath的實例詳解的相關資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-09-09

