Java Web文件上传
参考资料:http://www.cnblogs.com/xdp-gacl/p/4200090.html
一、问题描述
Java Web文件上传需要借助一些第三方库,常用的是借助Apache的包,有两个:
commons-fileupload
commons-io
二、前端代码示例
<form method="post" id="uploadApkForm" action="uploadapk"
enctype="multipart/form-data">
<p>
文件:<input name="apkFile" type="file" /> <!--有multiple属性时支持选中多个文件同时上传-->
</p>
<p>
版本:<input name="version" type="text" placeholder="请输入版本信息" />
</p>
</form>
注:
enctype="multipart/form-data" 是必须的,表示这是个含文件的form表单;
若type="file" 的 input标签含有 multiple 属性,则能够在弹出框中同时选中多个文件上传
三、后端代码示例
try {
// 判断enctype属性是否为multipart/form-data
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (!isMultipart) {// 不是文件上传,用传统方法获取数据
// String userId = request.getParameter("userId");等
return;
} // 为multipart/form-data
int maxMemorySize = 1024 * 1000 * 50;// 50MB
int maxRequestSize = 1024 * 1000 * 100;// 100MB
String projAbsolutePath = request.getRealPath("");
String uploadRelativeDir = "upload/apk/";
String uploadTmpRelativeDir = uploadRelativeDir + "/tmp/";
File uploadDirObj = new File(projAbsolutePath + uploadRelativeDir);
if (!uploadDirObj.exists()) {
uploadDirObj.mkdirs();
}
File uploadTmpDirObj = new File(projAbsolutePath + uploadTmpRelativeDir);
if (!uploadTmpDirObj.exists()) {
uploadTmpDirObj.mkdirs();
} // Create a factory for disk-based file items
DiskFileItemFactory factory = new DiskFileItemFactory(); // 当上传文件太大时,因为虚拟机能使用的内存是有限的,所以此时要通过临时文件来实现上传文件的保存
// 此方法是设置是否使用临时文件的临界值(单位:字节)
factory.setSizeThreshold(maxMemorySize);
// 与上一个结合使用,设置临时文件的路径(绝对路径)
factory.setRepository(uploadTmpDirObj); // Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload(factory); // 解决上传文件名的中文乱码,tomcat8不需要了
// upload.setHeaderEncoding("UTF-8"); // 设置上传内容的大小限制(单位:字节)
upload.setSizeMax(maxRequestSize); // Parse the request
List<FileItem> items = upload.parseRequest(request);
Iterator<?> iter = items.iterator();
String fieldName = null;
FileItem item = null;
String fileName = null;
String version = null;
while (iter.hasNext()) {
item = (FileItem) iter.next();
fieldName = item.getFieldName();
if (item.isFormField()) {// 普通表单字段,版本信息
version = item.getString();
System.out.println(fieldName + " " + version);
} else {// 文件字段
fileName = item.getName();
if (!fileName.endsWith("apk")) {// 不是apk文件
System.out.println("不是apk文件");
return;
}
fileName = fileName.substring(fileName.lastIndexOf("\\") + 1);
String contentType = item.getContentType();
boolean isInMemory = item.isInMemory();
long sizeInBytes = item.getSize();
System.out.println(fileName + " " + contentType + " " + isInMemory + " " + sizeInBytes);
item.write(new File(uploadDirObj, fileName));
}
}
apkInfoMapper.insertVersionInfo(version,
new SimpleDateFormat("yy-MM-dd HH:mm:ss").format(System.currentTimeMillis()),
(uploadRelativeDir + fileName));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
注:
为保证服务器安全,如果文件不提供下载,上传文件应该放在外界无法直接访问的目录下,如WEB-INF目录下;
不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,而有些只是单纯的文件名,要处理获取到的上传文件的文件名的路径部分,只保留文件名部分;
为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名
四、扩展:利用SpringMVC实现单文件/多文件上传下载
package com.mucfc; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID; import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartResolver; @Controller
@RequestMapping("/file")
public class FileController { @RequestMapping("/toFile")
public String toFileUpload() {
return "fileUpload";
} @RequestMapping("/toFile2")
public String toFileUpload2() {
return "fileUpload2";
} /**
* 方法一上传文件
*/
@RequestMapping("/onefile")
public String oneFileUpload(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request,
ModelMap model) { // 获得原始文件名
String fileName = file.getOriginalFilename();
System.out.println("原始文件名:" + fileName); // 新文件名
String newFileName = UUID.randomUUID() + fileName; // 获得项目的路径
ServletContext sc = request.getSession().getServletContext();
// 上传位置
String path = request.getRealPath("/img") + "/"; // 设定文件保存的目录 File f = new File(path);
if (!f.exists())
f.mkdirs();
if (!file.isEmpty()) {
try {
FileOutputStream fos = new FileOutputStream(path + newFileName);
InputStream in = file.getInputStream();
int b = 0;
while ((b = in.read()) != -1) {
fos.write(b);
}
fos.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
}
} System.out.println("上传图片到:" + path + newFileName);
// 保存文件地址,用于JSP页面回显
model.addAttribute("fileUrl", "img/" + newFileName);
return "fileUpload";
} /**
* 方法二上传文件,一次一张
*/
@RequestMapping("/onefile2")
public String oneFileUpload2(HttpServletRequest request, HttpServletResponse response) throws Exception {
CommonsMultipartResolver cmr = new CommonsMultipartResolver(request.getServletContext());
if (cmr.isMultipart(request)) {
MultipartHttpServletRequest mRequest = (MultipartHttpServletRequest) (request);
Iterator<String> files = mRequest.getFileNames();
while (files.hasNext()) {
MultipartFile mFile = mRequest.getFile(files.next());
if (mFile != null) {
String fileName = UUID.randomUUID() + mFile.getOriginalFilename();
String path = request.getRealPath("") + "/img/" + fileName;
System.out.println(path);
File localFile = new File(path);
if (!localFile.exists()) {
localFile.createNewFile();
}
mFile.transferTo(localFile);
request.setAttribute("fileUrl", "img/" + fileName);
}
}
}
return "fileUpload";
} /**
* 一次上传多张图片
*/
@RequestMapping("/threeFile")
public String threeFileUpload(@RequestParam("file") CommonsMultipartFile files[], HttpServletRequest request,
ModelMap model) { List<String> list = new ArrayList<String>();
// 获得项目的路径
ServletContext sc = request.getSession().getServletContext();
// 上传位置
String path = sc.getRealPath("/img") + "/"; // 设定文件保存的目录
File f = new File(path);
if (!f.exists())
f.mkdirs(); for (int i = 0; i < files.length; i++) {
// 获得原始文件名
String fileName = files[i].getOriginalFilename();
System.out.println("原始文件名:" + fileName);
// 新文件名
String newFileName = UUID.randomUUID() + fileName;
if (!files[i].isEmpty()) {
try {
FileOutputStream fos = new FileOutputStream(path + newFileName);
InputStream in = files[i].getInputStream();
int b = 0;
while ((b = in.read()) != -1) {
fos.write(b);
}
fos.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("上传图片到:" + path + newFileName);
list.add("img/" + newFileName); }
// 保存文件地址,用于JSP页面回显
model.addAttribute("fileList", list);
return "fileUpload2"; } /**
* 列出所有的图片
*/
@RequestMapping("/listFile")
public String listFile(HttpServletRequest request, HttpServletResponse response) {
// 获取上传文件的目录
ServletContext sc = request.getSession().getServletContext();
// 上传位置
String uploadFilePath = sc.getRealPath("/img") + "/"; // 设定文件保存的目录
// 存储要下载的文件名
Map<String, String> fileNameMap = new HashMap<String, String>();
// 递归遍历filepath目录下的所有文件和目录,将文件的文件名存储到map集合中
listfile(new File(uploadFilePath), fileNameMap);// File既可以代表一个文件也可以代表一个目录
// 将Map集合发送到listfile.jsp页面进行显示
request.setAttribute("fileNameMap", fileNameMap);
return "listFile";
} public void listfile(File file, Map<String, String> map) {
// 如果file代表的不是一个文件,而是一个目录
if (!file.isFile()) {
// 列出该目录下的所有文件和目录
File files[] = file.listFiles();
// 遍历files[]数组
for (File f : files) {
// 递归
listfile(f, map);
}
} else {
/**
* 处理文件名,上传后的文件是以uuid_文件名的形式去重新命名的,去除文件名的uuid_部分
* file.getName().indexOf
* ("_")检索字符串中第一次出现"_"字符的位置,如果文件名类似于:9349249849-88343-8344_阿_凡_达.avi
* 那么file.getName().substring(file.getName().indexOf("_")+1)
* 处理之后就可以得到
*/
String realName = file.getName().substring(file.getName().indexOf("_") + 1);
// file.getName()得到的是文件的原始名称,这个名称是唯一的,因此可以作为key,realName是处理过后的名称,有可能会重复
map.put(file.getName(), realName);
}
} @RequestMapping("/downFile")
public void downFile(HttpServletRequest request, HttpServletResponse response) {
System.out.println("1");
// 得到要下载的文件名
String fileName = request.getParameter("filename");
System.out.println("2");
try {
fileName = new String(fileName.getBytes("iso8859-1"), "UTF-8");
System.out.println("3");
// 获取上传文件的目录
ServletContext sc = request.getSession().getServletContext();
System.out.println("4");
// 上传位置
String fileSaveRootPath = sc.getRealPath("/img"); System.out.println(fileSaveRootPath + "\\" + fileName);
// 得到要下载的文件
File file = new File(fileSaveRootPath + "\\" + fileName); // 如果文件不存在
if (!file.exists()) {
request.setAttribute("message", "您要下载的资源已被删除!!");
System.out.println("您要下载的资源已被删除!!");
return;
}
// 处理文件名
String realname = fileName.substring(fileName.indexOf("_") + 1);
// 设置响应头,控制浏览器下载该文件
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(realname, "UTF-8"));
// 读取要下载的文件,保存到文件输入流
FileInputStream in = new FileInputStream(fileSaveRootPath + "\\" + fileName);
// 创建输出流
OutputStream out = response.getOutputStream();
// 创建缓冲区
byte buffer[] = new byte[1024];
int len = 0;
// 循环将输入流中的内容读取到缓冲区当中
while ((len = in.read(buffer)) > 0) {
// 输出缓冲区的内容到浏览器,实现文件下载
out.write(buffer, 0, len);
}
// 关闭文件输入流
in.close();
// 关闭输出流
out.close();
} catch (Exception e) { }
}
}
原理:Servlet或Spring(实际上也是依赖Servlet)将收到的文件持久化到默认的临时磁盘目录(用户也可以指定),代码中获取到的File对象即是存在该临时目录的文件对象
另外,关于HTTP POST提交数据的三种常用方式(application/x-www-form-urlencoded、multipart/form-data、application/json)的区别可参阅:https://imququ.com/post/four-ways-to-post-data-in-http.html
五、Spring文件上传
两种方法:
法1:通过@RequestParameter
法2:通过@RequestPart
两种都是用来接收multipart/form-data请求传来的数据,区别:前者可以同时接收文件(multipart)域和普通键值对参数,而后者可以同时接收文件域和更复杂的对象(如json、xml等)。
可参阅:https://stackoverflow.com/questions/16230291/requestpart-with-mixed-multipart-request-spring-mvc-3-2
Java Web文件上传的更多相关文章
- Java Web文件上传原理分析(不借助开源fileupload上传jar包)
Java Web文件上传原理分析(不借助开源fileupload上传jar包) 博客分类: Java Web 最近在面试IBM时,面试官突然问到:如果让你自己实现一个文件上传,你的代码要如何写,不 ...
- java web 文件上传下载
文件上传下载案例: 首先是此案例工程的目录结构:
- Java web文件上传下载
[版权申明:本文系作者原创,转载请注明出处] 文章出处:http://blog.csdn.net/sdksdk0/article/details/52048666 作者:朱培 ID:sdksdk0 邮 ...
- [转]java web 文件上传
实现WEB开发中的文件上传功能,需完成如下二步操作: 在WEB页面中添加上传输入项,<input type=“life” name=“”>,使用时注意: 1. 必须要设置 ...
- java Web 文件上传
注意:请求实体过大的问题,请修改Nginx服务器的大小(百度参考413 Request Entity Too Large 的解决方法)jsp:<input type="file&quo ...
- java进行文件上传,带进度条
网上看到别人发过的一个java上传的代码,自己写了个完整的,附带源码 项目环境:jkd7.tomcat7. jar包:commons-fileupload-1.2.1.jar.commons-io-1 ...
- servlet web文件上传
web文件上传也是一种POST方式,特别之处在于,需设置FORM的enctype属性为multipart/form-data. 并且需要使用文件域. servlet的代码比较关键是这几句: // 使用 ...
- java+大文件上传解决方案
众所皆知,web上传大文件,一直是一个痛.上传文件大小限制,页面响应时间超时.这些都是web开发所必须直面的. 本文给出的解决方案是:前端实现数据流分片长传,后面接收完毕后合并文件的思路. 实现文件夹 ...
- H5+JAVA的文件上传,断点续传
这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时候,向后端传入参数:当前为第几块文件,和分片总数 下面直接贴代码吧,一些难懂的我大部分都加上注释了: 上传文件实体类: 看得 ...
随机推荐
- jquery.Deferred promise解决异步回调
我们先来看一下编写AJAX编码经常遇到的几个问题: 1.由于AJAX是异步的,所有依赖AJAX返回结果的代码必需写在AJAX回调函数中.这就不可避免地形成了嵌套,ajax等异步操作越多,嵌套层次就会越 ...
- [Bug]redis问题解决(MISCONF Redis is configured to save RDB snapshots)
redis问题解决(MISCONF Redis is configured to save RDB snapshots) (error) MISCONF Redis is configured t ...
- Linux 网络编程二(Socket创建)
TCP通信 一个程序使用套接字需要执行4个步骤. --分配套接口和初始化 --连接 --发送或接收数据 --关闭套接字 涉及到的调用包括socket.bind.listen.connect(阻塞线程) ...
- 构架高性能WEB网站的几点知识
前言: 对于构架高性能的web网站大家都很感兴趣,本文从几点粗谈高性能web网站需要考虑的问题. HTML静态化 什么是html静态化? 说得简单点,就是把所有不是.htm或者.html的页面改为.h ...
- http状态码代表含义
状态代码 状态信息 含义 100 Continue 初始的请求已经接受,客户应当继续发送请求的其余部分.(HTTP 1.1新) 101 Switching Protocols 服务器将遵从客户的请求转 ...
- 3.SQLAlchemy文档-SQLAlchemy Core(中文版)
这里的文描述了关于SQLAlchemy的的SQL渲染引擎的相关内容,包括数据库API的集成,事务的集成和数据架构描述服务.与以领域为中心的ORM使用模式相反,SQL表达式语言提供了一个数据构架为中心的 ...
- 九幽史程博:助力国内开发者借Win10东风出海
微软Biuld2016大会刚刚结束,会议上微软CEO纳德拉Show出的一大波黑科技,又一次让软粉们心情为之振奋,信仰充值爆棚! 尽管过去一年微软的Win10 Mobile表现不尽如人意,可是凭借PC端 ...
- 魅蓝Note2 在Android Studio 与 Eclipse中无法被检测到
昨天到手的Note2 结果发现测试不了,一看魅蓝的版本是android 5.1,然后更新的自己的SDK. 最后…… 仍然不能识别到手机. ———————————— 今天在stackoverflow上搜 ...
- 【JVM】模板解释器--如何根据字节码生成汇编码?
1.背景 仅针对JVM的模板解释器: 如何根据opcode和寻址模式,将bytecode生成汇编码. 本文的示例中所使用的字节码和汇编码,请参见上篇博文:按值传递还是按引用? 2.寻址模式 本文不打算 ...
- 关于python中PIL的安装
python 的PIL安装是一件很蛋痛的事, 如果你要在python 中使用图型程序那怕只是将个图片从二进制流中存盘(例如使用Scrapy 爬网存图),那么都会使用到 PIL 这库,而这个库是出名的难 ...