java基于servlet实现文件上传功能解析

最近项目需要做一个文件上传功能,做完了分享下,顺带当做笔记。

上传功能用后台用java实现,前端主要是js的ajax实现。后台还加入定时删除临时文件。

效果如图

首先是上传功能的主要类,下面是代码

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;

//定义合法后缀名的数组

private String[] allowedExtName=new String[]

{"zip","rar",//压缩文件

"txt","doc","wps","docx","java",//文本

"xls","xlsx",//表格

"ppt","pptx",//幻灯片

"pdf",//pdf

"jpg","jpeg","bmp","gif","png"//图片

};

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;

//创建工厂对象和文件上传对象

DiskFileItemFactory factory=new DiskFileItemFactory();

ServletFileUpload upload=new ServletFileUpload(factory);

//创建上传监听器和设置监听器

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());

//如果文件夹不存在则创建

if (!dirFile .exists() && !dirFile .isDirectory())

{

dirFile .mkdir();

}

//根据日期创建文件夹,保存到对应日期的文件夹下

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="上传文件出现错误:"+e.getMessage();

}

if(!error.equals("")){

System.out.println(error);

session.setAttribute("error", error);

}else{

session.setAttribute("result", "OK");

session.setAttribute("filename",upFileName);

}

}

/**

* 为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名

* @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接口,我们再写一个类实现这个接口,上面类中添加该接口

//创建工厂对象和文件上传对象

DiskFileItemFactory factory=new DiskFileItemFactory();

ServletFileUpload upload=new ServletFileUpload(factory);

//创建上传监听器和设置监听器

UploadListener listener=new UploadListener();

session.setAttribute("LISTENER", listener);

upload.setProgressListener(listener);

下面是这个监听类的具体实现代码

package util.upload;

import org.apache.commons.fileupload.ProgressListener;

public class UploadListener implements ProgressListener{

private volatile long

bytesRead = 0L,//上传的字节数

contentLength = 0L,//总字节数

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;

}

}

现在能获取上传进度等信息了,但还需要一个servlet返回给前端,下面实现

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();//上传的字节数

contentLength = listener.getContentLength();//总字节数

}

//自己定义的返回格式

String rp=bytesRead+","

+contentLength+","

+error+","

+result+","

+fileName;

//System.out.println(rp);

out.print(rp);

/* //返回json格式数据

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();

}

}

}

后台上传的功能代码写完了,下面实现上传的前端,首先是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;//发送上传请求的XMLHttpRequest对象

var httpProgress = null;//发送请求进度信息的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发送上传文件,获取上传进度,结果等信息。

使用的html5的file API,所以必须ie9以上的才可以兼容,火狐还有个问题,ajax请求不立即返回,直到所有ajax请求都发送完了,才都返回同一个结果,这就导致上传进度不显示。不过上传进度信息也可以使用html5的file api获取,其中加了一点代码,页面下面test的div里的数值就是在前端获取到的进度。

上传的都实现完了,接着是处理上传后的临时文件,因为使用的uuid命名文件,所以文件会生成很多,没用的需要定时处理。使用ServletContextListener:

在 Servlet API 中有一个 ServletContextListener 接口,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期。

当Servlet 容器启动或终止Web 应用时,会触发ServletContextEvent 事件,该事件由ServletContextListener 来处理。在 ServletContextListener 接口中定义了处理ServletContextEvent 事件的两个方法。

利用其特性,实现定时删除临时文件的功能,代码如下:

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;

/**

* 时间监听器

*

*

*/

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();

}

}

}

// 监听器初始方法

public void contextInitialized(ServletContextEvent sce) {

timer = new Timer();

systemTask = new SystemTaskTest(sce.getServletContext()

.getRealPath("/"), sce.getServletContext());

try {

System.out.println("定时器已启动");

// 监听器获取网站的根目录

String path = sce.getServletContext().getRealPath("/");

Long time = Long.parseLong(every_time_run) * 1000;// 循环执行的时间

System.out.println("time" + time);

// 第一个参数是要运行的代码,第二个参数是从什么时候开始运行,第三个参数是每隔多久在运行一次。重复执行

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;

}

/**

* 把要定时执行的任务就在run中

*/

public void run() {

TempFileManager etf;

try {

System.out.println("开始执行任务!");

// 需要执行的代码

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

etf = new TempFileManager(path);

etf.run();

System.out.println("指定任务执行完成!");

} catch (Exception e) {

e.printStackTrace();

}

}

}

上面只是监听器,负责定时调用删除临时文件的方法,具体实现是下面的类

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();

}

}

}

/**

* 构造函数。初始化参数

* @param path

*/

public TempFileManager(String path) {

this.path = path;

}

/**

* 把线程要执行的代码放在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程序设计有所帮助。

以上是 java基于servlet实现文件上传功能解析 的全部内容, 来源链接: utcz.com/p/209572.html

回到顶部