我们做上传头像功能,图片很小,可以使用第一篇中的方法,甚至可以直接使用Base64,以字符串的形式来上传。不过对一些更大的功能,如发帖,发说说等,要求图片比较大,也要求同时上传多张。对于上传一些大的文件,现在都是用的Apache的框架commons-fileupload,不得不说Apache真的是一个伟大的组织,几乎所有的框架,典型的如SpringMvc上传文件,也是使用的commons-fileupload。那么这里我们来看看如何直接使用commons-fileupload来上传文件吧。
一、学习要点
- 如何使用commons-fileupload
- 如何实现多个文件上传
- 如何限定文件的格式与数量
- 封装成一个工具类
二、commons-fileupload的使用
commons-fileupload的使用其实很简单:
①导入jar包
需要commons-io和commons-fileupload两个jar包,官网下载较慢,读者也可以自行去其他网站下载。
官网下载地址:http://commons.apache.org/proper/commons-fileupload/
②得到硬盘工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
③创建上传类
ServletFileUpload upload = new ServletFileUpload(factory);
④获取上传的Item(需要传入HttpServletRequest对象)
List<FileItem> itemList = upload.parseRequest(request);
⑤遍历Item列表
for (FileItem item : itemList) {
if (item.isFormField()) {//是表单属性
//获取表单传进来的参数
} else {//是文件
//保存上传的文件
}
}
对于表单参数具体操作:
//获取表单名
String name=item.getFieldName();
//获取表单值
String content=item.getString("utf-8");
对于文件的具体操作:
//获取文件名
String fileName = item.getName();
//创建存放位置
String saveName = CommUtils.getUuidCode() + dotExtendName;
//保存文件(以流的形式写到服务器上)
File saveFile = new File(savePath + saveName);
FileOutputStream out = new FileOutputStream(saveFile);
InputStream in = item.getInputStream();
int len;
while ((len = in.read()) != -1) {
out.write(len);
}
out.flush();
的确,使用方法还是简单的一匹的,其中有有一些异常需要抛出一下,下面会给出完整代码。
三、如何上传多个文件
我们可以看到,上传实际是通过遍历一个列表,那么我们对列表的每个Item进行遍历,将是文件类型的Item都分别保存下来,就实现了多张上传。
不过这里有一个问题,需要解决:我们一般在记录用户的图片的时候,为了提高数据库查询效率,通常都是将图片存在文件服务器,而数据库只保存图片的名称,这时候上传文件重名了怎么办?两个不同的用户的头像名称一样了,怎么知道谁该用哪一张?
答案很简单:我们重新给每个文件制定名字,保证我们上传的图片都有一个独一无二的名称。
①使用当前毫秒数+随机数命名
这种方式也存在安全性,万一两个用户在同一毫秒请求,且随机数刚好巧合了(虽然几率很小,但是可能发生),不推荐。
②使用uuid算法
uuid算法可以保证同一台机器,得到不同的名称。推荐使用。
public static String getUuidCode() {
return UUID.randomUUID().toString().replace("-", "");
}
名称问题搞定了,上传多张也就不成问题了
四、如何限定文件格式与数量
我们遍历了Item列表,就可以对其文件后缀名进行检查,不合格就抛出异常,便达到了限定格式的效果。同样,只要判断Item列表的size就可以限定数量了,不过值得注意的是,限定的是表单与文件的总数量。
五、封装成util,完整代码
我们从异常、dao、service、action等层次,以用户上传头像为例完整地描述一次文件上传过程。
①异常
public class MyFileUploadException extends RuntimeException {
public MyFileUploadException(String message, Throwable cause) {
super(message, cause);
}
public MyFileUploadException(String message) {
super(message);
}
}
②工具类
import com.photograph_u.exception.MyFileUploadException;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FileUploadUtil {
public static void upload(HttpServletRequest request, String savePath, int fieldCount, int fileCount) {
Map<String, Object> parameterMap = new HashMap<>();
List<String> fileList = new ArrayList<>();
FileOutputStream out = null;
InputStream in = null;
if (!ServletFileUpload.isMultipartContent(request)) {
throw new MyFileUploadException("表单属性设置不正确");
}
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(16 * 1024);//占用运行内存16K,多余的存内存
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setSizeMax(5 * 1024 * 1024);//总大小限制5M
upload.setFileSizeMax(700 * 1024);//单张限制700K
File dir = new File(savePath);
if (!dir.exists()) {
dir.mkdirs();//创建目录(允许多级)
}
List<FileItem> itemList;
try {
itemList = upload.parseRequest(request);//获取多重表单
} catch (FileUploadException e) {
throw new MyFileUploadException("文件上传异常", e);
}
//检查文件数量
if (itemList.size() > (fieldCount + fileCount)) {
throw new MyFileUploadException("文件数量过多");
}
//遍历表单
for (FileItem item : itemList) {
if (item.isFormField()) {//是表单属性
try {
parameterMap.put(item.getFieldName(), item.getString("utf-8"));
} catch (UnsupportedEncodingException e) {
throw new MyFileUploadException("多重表单编码异常", e);
}
} else {//是文件
String fileName = item.getName();
if (!"".equals(fileName)) {//选择了文件
//检查文件类型是否正确
String dotExtendName = fileName.substring(fileName.lastIndexOf("."));
boolean allow = false;
String[] allowType = {".jpg", ".png", ".bmp", ".gif"};
for (String type : allowType) {
if (type.equals(dotExtendName))
allow = true;
}
if (!allow) {
throw new MyFileUploadException("文件类型不支持");
}
//保存上传文件
String saveName = CommUtils.getUuidCode() + dotExtendName;
File saveFile = new File(savePath + saveName);
try {
out = new FileOutputStream(saveFile);
in = item.getInputStream();
int len;
while ((len = in.read()) != -1) {
out.write(len);
}
out.flush();
fileList.add(saveName);
parameterMap.put("files", fileList);
} catch (Exception e) {
throw new MyFileUploadException("文件上传异常", e.getCause());
} finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException e) {
throw new MyFileUploadException("文件上传异常", e);
}
}
}
}
}
request.setAttribute("parameterMap", parameterMap);
}
}
③dao层
//修改头像
public boolean updateHeadImage(int userId, String headImage) {
String sql = "update user set head_image=? where id=? and is_deleted=0";
int count = update(sql, new Object[]{headImage, userId});
return count == 1;
}
④service层
//设置头像
public boolean setHeadImage(int userId, String headImage) {
UserDao userDao = new UserDao();
return userDao.updateHeadImage(userId, headImage);
}
④action层
//设置头像
public void setHeadImage(HttpServletRequest request, HttpServletResponse response) throws IOException {
MyResponse<String> myResponse = new MyResponse<>();
int userId = (int) request.getSession().getAttribute("user_id");
FileUploadUtil.upload(request, FileUriConsts.HEAD_IMAGE_URI, 0, 1);
Map parameterMap = (Map) request.getAttribute("parameterMap");
List fileList = (List) parameterMap.get("files");
UserService service = new UserService();
if (service.setHeadImage(userId, (String) fileList.get(0))) {
myResponse.setCode(0);
myResponse.setMessage("头像设置成功");
} else {
myResponse.setCode(1);
myResponse.setMessage("头像设置失败");
}
ResponseUtil.sendResponse(response, myResponse);
}
⑤文件保存路径
public class FileUriConsts {
/**
* windows下的路径
*/
// public static final String HEAD_IMAGE_URI = "D:/photograph_u/head_images/";
/**
*linux下的路径
*/
public static final String HEAD_IMAGE_URI = "/var/lib/tomcat7/webapps/photograph_u/head_images/";
}
⑥Html写法
上传头像
<form action="user/setHeadImage" enctype="multipart/form-data" method="post">
<input type="file" name="file">
<input type="submit" value="上传">
</form>