整理了一下jsp的上传下载,由客户端到服务端,包括一些常规的业务
一客户端
先看最基本的情况
1 在表单设置multipart/form-data,通过提交键把数据和附件一次性提交的。服务器更多的是对应这个例子。
但有以下问题
一般的Ajax无法把附件数据发过去
附件往往涉及比较多问题,比如上传需要时间(要有个进度),上传也有可能不成功等,上传需要校验的东西就比较多。
2于是就有把上传分开来自己执行的。上传项在浏览文件后就开始上传,其他表单数据在提交表单时再提交。
更详细地说是:上传在表单中有自成一体的界面,浏览后点击上传开始上传(也可以是其他形式),以ajax方式发送。发送在界面显示上传状态,并把file类型的控件的值置空。在提交表单时,仅提交其他数据。
3在表单一次性的提交的,但是用了ajax。这种我没做通,但想想这种在现实中很少需要就没做了。
之后就说说一种可行的实现,使用了jquery file Upload。
Jquery file Upload(鉴于官方文档不是一般的乱,整理了一下关键的页面):
Hello world:http://bbs.9ria.com/thread-245293-1-1.html
官方文档:https://github.com/blueimp/jQuery-File-Upload/wiki
Option:https://github.com/blueimp/jQuery-File-Upload/wiki/Options
关于使用思路(不关注jquery file Upload可以跳过):
首先我比较关注上传的js部分,所以没去了解界面的部分。jquery file Upload应该是界面做得最好的。就上传插件而言,基本思路应该是先配置插件的option,之后触发文件上传(怎么触发就是掌握不同插件的关键)
jquery file Upload在这一点比较奇葩,它是默认点击input就自动上传的。它可以把上传触发绑定到一个事件上,通过add这个option来配置。但这其实是有问题的,比如表单有两个上传域,希望通过某按键的click事件触发上传,click事件的处理就会被调用两次。结论很简单,用jquery file Upload,不要把多个文件的上传绑在一起,让他们各自完成。
jquery file Upload每次只会像服务器发一个文件,开始我也非常疑惑。但后来发现其他插件也是这样的。还是上面的一条,让每个上传各自完成。
其他好用的上传插件还有
Plupload:功能上最强,有校验,上传多个,拖拽上传等功能。(file类型域不是自己写的,通过指定一个button,由它的点击事件生成,通过调函数来开始上传)
文档:http://www.cnblogs.com/2050/p/3913184.html#plupload_doc4
Demo:http://chaping.github.io/plupload/demo/index.html
二服务端
客户端怎么做在服务端的接口都是一样的。都是面对着这么一个表单,表单里有一些字段,还有一个到多个的上传附件。
纵观附件的管理,业务流程是这样的
上传要处理的问题:
附件重名处理
上传类型检查、大小等限制
上传文件的位置规划
文件夹及安全性
附件数据库
编码问题
上传方案
1最基本,用request的inputstream读写
2用组件库,common-fileupload(例子里用了这个),这些组件库对request进行封装。
3用struct提供的上传功能
例子中的处理过程
用common-uploadfile,需要以下包。自己写了一个工具类,改进自
http://lohasle.iteye.com/blog/1604428
上传文件夹放在网站根目录,可以用工具类把所有附件都传到一个目录,也可以按表单filedname配置上传的位置。
上传的文件会以一个随机的名字保存,避免了附件重名。
用MineType检查文件类型,查询minetype可点击
http://www.cnblogs.com/newcj/archive/2011/08/10/2134305.html
先把文件传到一个临时文件夹,用fileItem对象读取文件信息,再重命名为随机名,传到指定文件夹。
fileUpload几个要注意的问题
fileUpload关键就是使用FileItem类,可以查文档
文件名,内容乱码:http://zsw4522006.iteye.com/blog/1470949
首先文件在上传和下载过程中是按字节传的,编码由文件本身保存所以不会乱码。
但文件名会有乱码,fileItem的getString也会有乱码,则主要是fileUpload默认的和现实的编码不一样。
common-fileupload对request进行封装,一旦我们按流的方式打开http请求,就不能用getParameter方法,而要用common-fileupload提供的接口访问表单
下载、删除和使用
当把“上传文件的位置规划”和“数据库存储”解决,后面的问题就差不多了。毕竟文件已经在那里了,它的信息也详细记录在数据库。实现详见代码
最后上代码
无ajax的附件表单,UploadAddInput.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>上传文件</title>
</head> <body> <form name="upform" action="UploadAdd.jsp" method="POST" enctype="multipart/form-data">
<input type ="text" name="name"/><br/>
<input type ="file" name="file1"/><br/>
<input type ="file" name="file2"/><br/>
<input type="submit" value="Submit" /><br/>
<input type="reset" />
</form> </body>
</html>
Upload实体类,Upload.java
package com.xcat.upload; import java.util.Date; public class Upload {
private int id;
private String fileName;
private String description;
private String loc;
private String mineType;
//单位KB;
private int fileSize;
private Date upTime;
private int owner;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public String getMineType() {
return mineType;
}
public void setMineType(String mineType) {
this.mineType = mineType;
}
public int getFileSize() {
return fileSize;
}
public void setFileSize(int fileSize) {
this.fileSize = fileSize;
}
public Date getUpTime() {
return upTime;
}
public void setUpTime(Date upTime) {
this.upTime = upTime;
}
public int getOwner() {
return owner;
}
public void setOwner(int owner) {
this.owner = owner;
} }
上传工具类,参考http://lohasle.iteye.com/blog/1604428,UploadUtil.java
package com.xcat.upload; import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random; import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import javax.servlet.http.HttpServletRequest; public class UploadUtil {
public static class PosConfig{
private String defaultPos;
private Map<String,String> pos =new HashMap<String,String>();
public String getDefaultPos() {
return defaultPos;
}
public void setDefaultPos(String defaultPos) {
this.defaultPos = defaultPos;
}
public Map<String, String> getPos() {
return pos;
}
public void setPos(Map<String, String> pos) {
this.pos = pos;
} }
/*
* 常量
*/
//一兆的大小
public static final long TRILLION = 1024 * 1024;
//文件限制性的大小(默认的5M)
public static final long DEFAULT_SIZE = TRILLION * 5;
//试探随机文件名的最大次数
public static final int RANDOM_NAME_TIMES =2000; /*
* 参数
*/
//限制的文件类型
private String[] allowMineType;
// 限制的文件大小,默认5M,-1就是无限制大小
private long fileMaxSize = DEFAULT_SIZE;
// 设置缓冲区文件夹
private String cachePathString;
// 设置缓冲区大小,默认5M
private long cacheSize = DEFAULT_SIZE;
// 客户端文件系统字符编码
private String clientCharset = "utf-8";
// request表单的字符编码
private String formCharset = "utf-8"; //上传位置配置
private PosConfig posConfig; /*
* 变量
*/
//request
private HttpServletRequest request;
// 文件上传处理类
private ServletFileUpload sfu;
// 磁盘工厂
private DiskFileItemFactory factory = new DiskFileItemFactory();
// 表单域的值
private Map<String, String> formMap = new HashMap<String, String>();
// 上传文件信息
private Map<String, Upload> fileMap = new HashMap<String, Upload>();
//上传的附件是否传到多个位置
private boolean multiPos = false; public UploadUtil() {
} public Map<String, String> getFormMap() {
return formMap;
} public void setFormMap(Map<String, String> formMap) {
this.formMap = formMap;
} public String getCachePathString() {
return cachePathString;
} public void setCachePathString(String cachePathString) throws Exception {
this.cachePathString = cachePathString;
} public long getCacheSize() {
return cacheSize;
} public void setCacheSize(long cacheSize) {
this.cacheSize = cacheSize;
} public long getFileSize() {
return fileMaxSize;
} public void setFileSize(long fileSize) {
this.fileMaxSize = fileSize;
} public String[] getAllowMineType() {
return allowMineType;
} public void setAllowMineType(String[] allowMineType) {
this.allowMineType = allowMineType;
} public HttpServletRequest getRequest() {
return request;
} public void setRequest(HttpServletRequest request) {
this.request = request;
} public PosConfig getPosConfig() {
return posConfig;
} public void setPosConfig(PosConfig posConfig) {
this.posConfig = posConfig;
} public String getClientCharset() {
return clientCharset;
} public void setClientCharset(String clientCharset) {
this.clientCharset = clientCharset;
} public String getFormCharset() {
return formCharset;
} public void setFormCharset(String formCharset) {
this.formCharset = formCharset;
} public Map<String, Upload> getFileMap() {
return fileMap;
} public void setFileMap(Map<String, Upload> fileMap) {
this.fileMap = fileMap;
} public ServletFileUpload getServletFileUpload() {
if (sfu == null) {
return sfu = new ServletFileUpload(factory);
} else {
return sfu;
}
} /**
* // 得到用户ip地址
*
*
* @return ip地址
*/
public String getIpAddress() {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
/**
* // 得到一个随机文件名
*
*
* @return 文件名
*/
public String getRandomFileName(){
String str=""; String[] ips=getIpAddress().split(".");
int ipv=0;
for(int i=0;i<ips.length;i++) ipv+= Integer.parseInt(ips[i]) << (8*i); str+=String.format("%010d", ipv);
str+=new Date().getTime();
Random r = new Random();
str+=String.format("%03d", r.nextInt(999));
return str;
} /**
* // 保证目录存在
*
*
* @return
* @throws Exception
*/
public void ensureDir(String dir) throws Exception{
File file =new File(dir);
//如果文件夹不存在则创建
if(!file.exists() && !file.isDirectory())
if(!file.mkdirs()) throw new Exception("创建文件夹"+dir+"失败");
} /**
* // 得到文件类型
*
* @param file
* @return 文件后缀
*/
private String getFileExt(File file) {
return getFileExt(file.getName());
} /**
* // 得到文件类型
*
* @param fileName
* @return 文件后缀
*/
private String getFileExt(String fileName) {
return fileName.substring(fileName.lastIndexOf(".") + 1);
} /*
* //根据路径和扩展名,在改路径下获得一个可用的文件名
* @param dir:路径,ext:扩展名
* @return 文件对象
*
*/
public File getRandomFile(String dir,String ext) throws Exception{
File file;
for(int i=0; i< RANDOM_NAME_TIMES ;i++){
file = new File(dir,getRandomFileName()+"."+ext);
if (!file.exists()) return file;
}
throw new Exception("上传繁忙");
} /*
* //判断mineType是不是在allowMineType中
* @param mineType
* @return
*
*/
public boolean validateMineType(String mineType) {
if (allowMineType == null) {
return true;
}
for (int i = 0, len = allowMineType.length; i < len; i++) {
if (allowMineType[i].equalsIgnoreCase(mineType)) {
return true;
}
}
return false;
} /*
* //所有表单附件都上传到dir
* @param dir
* @return
*
*/
public void uploadFiles(String dir) throws Exception {
multiPos=false;
posConfig =new PosConfig();
posConfig.defaultPos=dir;
uploadFiles();
} /*
* //按照路径配置来上传
* @param posConfig
* @return
*
*/
public void uploadFiles(PosConfig posConfig) throws Exception {
multiPos=true;
this.posConfig=posConfig;
uploadFiles();
} /**
* 文件上传 参数urlString是具体指定的目录 如果该对象属性值为空
* 将不使用缓存,无文件类型限制,上传大小默认为5M,目录规则默认为没有目录递归创建 相同文件名将覆盖源文件
* 此方法如文件上传错误或者文件类型不匹配将抛出异常
*
* @param request
* 当前请求
* @param urlString
* urlString是具体指定的目录
*/
private void uploadFiles() throws Exception {
if (!ServletFileUpload.isMultipartContent(request)) return;
ensureDir(cachePathString);
if(posConfig.defaultPos!=null)
ensureDir(posConfig.defaultPos); ServletFileUpload sfu = getServletFileUpload();
sfu.setHeaderEncoding(clientCharset);
sfu.setFileSizeMax(fileMaxSize);
if (cachePathString != null) {
factory.setRepository(new File(cachePathString));
factory.setSizeThreshold((int) cacheSize);
}
List<FileItem> items = sfu.parseRequest(request); for (FileItem ft : items) {
if (!ft.isFormField()) {
if(allowMineType!=null && !validateMineType(ft.getContentType()))
throw new Exception("上传文件类型错误");
else{
String dir;
if(multiPos && posConfig.pos.containsKey(ft.getFieldName())) {
dir=posConfig.pos.get(ft.getFieldName());
ensureDir(dir);
}
else dir= posConfig.defaultPos;
File file = getRandomFile(dir,getFileExt(ft.getName()));
ft.write(file); Upload upload=new Upload();
upload.setFileName(ft.getName());
upload.setLoc(file.getAbsolutePath());
upload.setMineType(ft.getContentType());
upload.setFileSize((int)(file.length()/1024));
upload.setUpTime(new Date());
fileMap.put(ft.getFieldName(), upload);
}
ft.delete(); } else {
formMap.put(ft.getFieldName(), ft.getString(formCharset));
}
}
}
}
上传的业务,UploadAdd.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page import="com.xcat.upload.UploadUtil" %>
<%@ page import="com.xcat.upload.Upload" %>
<%@ page import="com.xcat.upload.UploadDao" %>
<%
out.clear();
UploadUtil fu = new UploadUtil();
fu.setRequest(request);
fu.setCachePathString(request.getSession().getServletContext().getRealPath("/images/temp"));
fu.setAllowMineType(new String[]{"image/jpeg","image/gif","text/plain","application/msword"});
try {
//全部上传到同一个目录
//fu.uploadFiles(request.getSession().getServletContext().getRealPath("/images/test"));
UploadUtil.PosConfig posConfig= new UploadUtil.PosConfig();
posConfig.setDefaultPos(request.getSession().getServletContext().getRealPath("/images/default"));
posConfig.getPos().put("file1", request.getSession().getServletContext().getRealPath("/images/test1"));
posConfig.getPos().put("file2", request.getSession().getServletContext().getRealPath("/images/test2"));
fu.uploadFiles(posConfig);
} catch (Exception e1) {
e1.printStackTrace();
} Map<String,String> fieldMap = fu.getFormMap();
Set<String> key1 = fieldMap.keySet();
for(String s:key1){
System.out.print("表单名称:"+s + "; ");
System.out.println("表单值:"+fieldMap.get(s));
} UploadDao uploadDao=new UploadDao();
Map<String,Upload> fileMap = fu.getFileMap();
Set<String> key2 = fileMap.keySet();
for(String s:key2){ System.out.print("表单名称:"+s + "; ");
System.out.println("文件名:"+fileMap.get(s).getFileName() + "; "+
"文件路径:"+fileMap.get(s).getLoc() + "; "+
"文件类型:"+fileMap.get(s).getMineType() + "; "); Upload upload=fileMap.get(s); upload.setDescription("");
upload.setOwner(0);
uploadDao.add(upload);
} out.print("[{\"name\":\"file.jpg\"}]");
%>
下载,DownLoad.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page import="com.xcat.upload.UploadDao" %>
<%@ page import="com.xcat.upload.Upload" %>
<%@ page import="java.io.File" %>
<%@ page import="java.io.OutputStream" %>
<%@ page import="java.io.FileInputStream" %>
<%@ page import="java.net.URLEncoder" %>
<%
int id=Integer.parseInt(request.getParameter("id"));
UploadDao uploadDao=new UploadDao();
Upload upload=uploadDao.loadById(id);
if(upload==null) System.out.print("下载文件不存在");
File file=new File(upload.getLoc()); response.reset();
response.setContentType(upload.getMineType());
String fileLoc = upload.getLoc();
String fileName = upload.getFileName();
fileName = URLEncoder.encode(fileName,"UTF-8");
response.addHeader("Content-Disposition","attachment;filename=" + fileName);
response.setHeader("Content_length", String.valueOf(file.length())); OutputStream outp = null;
FileInputStream in = null;
try
{
outp = response.getOutputStream();
in = new FileInputStream(fileLoc); byte[] b = new byte[1024];
int i = 0; while((i = in.read(b)) > 0)
{
outp.write(b, 0, i);
} outp.flush();
response.flushBuffer();
out.clear();
out = pageContext.pushBody();
}
catch(Exception e)
{
System.out.println("下载过程中出现读写错误!");
e.printStackTrace();
}
finally
{
if(in != null)
{
in.close();
in = null;
}
if(outp != null)
{
outp.close();
outp = null;
}
} %>
ajax上传客户端代码,UploadAddInputAjax.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>上传文件</title>
<script src="style/js/jquery_1.9.1.js"></script>
<script src="style/js/jquery.ui.widget.js"></script>
<script src="style/js/jquery.iframe-transport.js"></script>
<script src="style/js/jquery.fileupload.js"></script>
</head> <body> <form name="upform" action="UploadAdd.jsp" method="POST" enctype="multipart/form-data">
姓名:<input type ="text" name="name"/><br/>
年龄:<input type ="text" name="age"/><br/>
上传1:<input type ="text" id="file1_ct"/><input type ="file" name="file1" id="file1"/><br/>
上传2:<input type ="text" id="file2_ct"/><input type ="file" name="file2" id="file2"/><br/>
<input type="submit" value="Submit" /><br/>
<input type="reset" />
</form>
<script type="text/javascript">
$("input[type=file]").fileupload({
url:"UploadAdd.jsp",//文件上传地址,当然也可以直接写在input的data-url属性内
dataType: 'json',
formData: null,
multi: true,
done:function(e,result){
//返回的数据在result.result中,假设我们服务器返回了一个json对象
//console.log(JSON.stringify(result.result));
$("#"+$(this).attr("id")+"_ct").val(result.result[0].name);
}
}); </script> </body>
</html>
参考文章
fileUpload×2:http://developer.51cto.com/art/200907/133797.htm
http://lohasle.iteye.com/blog/1604428
smartUpload:http://www.cnblogs.com/elleniou/archive/2012/09/24/2700583.html
文件夹操作:http://www.open-open.com/lib/view/open1384162223821.html