servlet上传功能前端和后端的实现
Posted 姚__
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}
}
判断文件是否超时,超时就自动删除,并且能自动删除文件夹。时时保持上传临时文件夹下干干净净,处女座最爱。。。
还有个配置文件,代码里能看到使用了那些参数,就不贴了,功能很简单,但也做了很久,搜了很多资料,感谢互联网上的各位分享知识的大神,没有他们,就没有这一切。再次感谢各位,虽然忘了看的哪些大神的文章做出来的了。。。如有不对,请指出,谢谢阅读。
以上是关于servlet上传功能前端和后端的实现的主要内容,如果未能解决你的问题,请参考以下文章