文件上传
代码实现
index.jsp代码
<form enctype="multipart/form-data" action="/upload" method="post">
请选择你要上传的文件:<input type="file" name="file">
<input type="submit" value="上传文件">
</form>
处理器FileController
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@Controller
public class FileController {
//SpringMVC识别多媒体文件后,会使用CommonsMultipartFile类对象进行读取文件
@RequestMapping(value = "/upload",method = {RequestMethod.POST})
public String upload(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
//获取文件在客户端的真实名称
String filename = file.getOriginalFilename();
/*Tomcat默认将上传的文件存储在tomcat\bin目录下,在该目录下文件无法被访问,
为了文件可以被访问,可以将文件上传到项目的根目录下*/
//获取当前程序在tomcat中的运行目录
String realPath = request.getServletContext().getRealPath("/");
//File.separator这个代表系统目录中的间隔符,不要用"/"拼串,避免跨平台失败
File tfile = new File(realPath+File.separator+filename);
//输出文件的绝对路径
System.out.println(tfile.getAbsolutePath());
//存储文件
file.transferTo(tfile);
return "index";
}
}
在springMVC.xml配置文件中配置如下内容:
<!--配置文件上传解析器,解析CommonsMultipartFile对象-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
注意:
1.可以使用UUID类获取一个随机的id增加到文件名上避免文件名的重复,如下所示
String uuid = UUID.randomUUID().toString();
2.如果将文件将文件上传到项目的根目录下,如果项目重新部署,会造成上传的文件被覆盖;
可以将文件储存在本地非tomcat目录下,但是tomcat无法访问改文件,此时需要在tomcat中增加虚拟目录的配置;也可以将文件储存在文件服务器中。
显示文件上传进度
文件上传解析器CommonsMultipartResolver类通过parseRequest方法对文件进行解析,我们需要对该方法进行扩展,那么就需要自定义类继承CommonsMultipartResolver类并重写parseRequest方法,在原有的代码中,增加对文件上传进度的监听。
import org.apache.commons.fileupload.*;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
public class MyCommonsMultipartResolver extends CommonsMultipartResolver {
@Override
protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
String encoding = this.determineEncoding(request);
FileUpload fileUpload = this.prepareFileUpload(encoding);
//设置文件上传进度的监听器
MyProgressListener myProgressListener = new MyProgressListener(request.getSession());
fileUpload.setProgressListener(myProgressListener);
try {
List<FileItem> fileItems = ((ServletFileUpload)fileUpload).parseRequest(request);
return this.parseFileItems(fileItems, encoding);
} catch (FileUploadBase.SizeLimitExceededException var5) {
throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), var5);
} catch (FileUploadBase.FileSizeLimitExceededException var6) {
throw new MaxUploadSizeExceededException(fileUpload.getFileSizeMax(), var6);
} catch (FileUploadException var7) {
throw new MultipartException("Failed to parse multipart servlet request", var7);
}
}
}
自定义监听器
设置成员变量session,是为了将上传进度的数据存储到session域中,使处理器可以获取数据并返回给前台。
import org.apache.commons.fileupload.ProgressListener;
import javax.servlet.http.HttpSession;
public class MyProgressListener implements ProgressListener {
private HttpSession session;
public MyProgressListener(HttpSession session) {
this.session = session;
}
/**
* @param l - 已经处理的大小
* @param l1 - 文件总大小
* @param i - 处理文件的编号(客户端可能上传多个文件)
*/
@Override
public void update(long l, long l1, int i) {
String msg=String.format("文件总大小%s,已经上传的大小%s,正在处理第%s个文件",l,l1,i);
System.out.println(msg);
if (l==l1){
session.setAttribute("msg","ok");
}else{
session.setAttribute("msg",msg);
}
}
}
index.jsp代码
在前台需要设置定时器循环发送Ajax请求获取文件上传的进度。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<title>Title</title>
</head>
<body>
<form enctype="multipart/form-data" action="/upload" method="post">
请选择你要上传的文件:<input type="file" name="file">
<input type="submit" id="btn1" value="上传文件">
</form>
<h3 id="uploadState"></h3>
</body>
<script>
$(function() {
$("#btn1").click(function () {
var interval = setInterval(function () {
$.ajax({
type: "get",
url: "uploadState",
success: function (result) {
console.log(result);
$("#uploadState").text(result.msg);
//如果上传完毕,关闭定时器
if(result.msg=="ok"){
clearInterval(interval);
}
}
});
}, 100);
});
})
</script>
</html>
控制器中的uploadState方法
//注意使用@ResponseBody注解时,需要导入jackson依赖
@RequestMapping("/uploadState")
@ResponseBody
public Map<String,String> uploadState(HttpSession session){
Object obj = session.getAttribute("msg");
Map<String,String> map=new HashMap<>();
map.put("msg",obj.toString());
return map;
}
Ajax实现文件上传
index.jsp代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<title>Title</title>
</head>
<body>
请选择你要上传的文件:<input type="file" name="uploadAjaxFile" id="uploadAjaxFile" >
<input type="submit" id="btn2" value="上传文件">
<h3 id="uploadState"></h3>
</body>
<script>
$(function() {
//因为删除了表单,所以需要增加点击事件
$("#btn2").click(function () {
var formData=new FormData();
//通过增加角标可将$("#uploadAjaxFile")JQuery对象转换为js对象,通过files[0]取出上传的第一个文件
formData.append("file",$("#uploadAjaxFile")[0].files[0]);
$.ajax({
type: "post",
url: "uploadAjax",
data:formData,
contentType:false,
processData: false,
success: function (result) {},
//在beforeSend方法中发送请求请求文件上传的进度
beforeSend:processState()
});
});
})
function processState() {
var interval = setInterval(function () {
$.ajax({
type: "post",
url: "uploadState",
success: function (result) {
console.log(result);
$("#uploadState").text(result.msg);
if(result.msg=="ok"){
clearInterval(interval);
}
}
});
}, 1000);
}
</script>
</html>
处理器中的方法变化不大,处理文件上传请求的方法不需要返回视图,可返回JSON。
//后台无需返回视图,需要返回JSON
@RequestMapping(value = "/uploadAjax",method = {RequestMethod.POST})
@ResponseBody
public Map<String,String> uploadAjax(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
String filename = file.getOriginalFilename();
String realPath = request.getServletContext().getRealPath("/");
File tfile = new File(realPath+File.separator+filename);
file.transferTo(tfile);
HashMap<String, String> map = new HashMap<>();
map.put("msg","上传完毕");
return map;
}
//处理文件上传进度请求的处理方法
@RequestMapping("/uploadState")
@ResponseBody
public Map<String,String> uploadState(HttpSession session){
Object obj = session.getAttribute("msg");
Map<String,String> map=new HashMap<>();
map.put("msg",obj.toString());
return map;
}
多文件上传
index.jsp代码
提供多个<input type=“file”>标签
<form enctype="multipart/form-data" action="/uploadFiles" method="post">
请选择你要上传的文件:
<input type="file" name="file"><br>
<input type="file" name="file"><br>
<input type="file" name="file"><br>
<input type="submit" value="上传文件">
</form>
@RequestMapping(value = "/uploadFiles",method = {RequestMethod.POST})
@ResponseBody
//使用数组接收上传的文件,循环解析数组中的文件
public String uploadFiles(@RequestParam("file") CommonsMultipartFile[] files, HttpServletRequest request){
for (CommonsMultipartFile file : files) {
String filename = file.getOriginalFilename();
String realPath = request.getServletContext().getRealPath("/");
File tfile = new File(realPath+File.separator+filename);
try {
file.transferTo(tfile);
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("多文件上传完毕");
return "index";
}
往往在文件上传时会在数据库中存储上传文件的名称、存放位置、大小、上传时间等…
文件下载
JavaWeb方式文件下载
@RequestMapping(value = "/download")
public void download(String filename, HttpServletRequest request, HttpServletResponse response) throws IOException {
String realPath = request.getServletContext().getRealPath("/");
File file = new File(realPath + File.separator + filename);
//设置响应类型为二进制内容
response.setContentType("application/octet-stream");
//设置内容描述,在响应头中设置
response.setHeader("Content-Disposition","attachment;filename="+filename);
//读取存在于项目根路径下的文件
FileInputStream in = new FileInputStream(file);
//通过response获取的字节流写出数据
ServletOutputStream out = response.getOutputStream();
byte[] bytes = new byte[1024];
int len=0;
while ((len=in.read(bytes))!=-1){
out.write(bytes,0,len);
}
out.close();
in.close();
}
运行程序,在地址栏输入"/download?filename=index.jsp"向服务器发送请求,便可以下载index.jsp文件。
返回值为ResponseEntity对象实现文件下载
@RequestMapping(value = "/download/{filename}/{ext}")
public ResponseEntity download(@PathVariable("filename") String filename,@PathVariable("ext") String ext, HttpServletRequest request) throws IOException {
String realPath = request.getServletContext().getRealPath("/");
File file = new File(realPath + File.separator + filename+"."+ext);
//将文件内容读取为字节数组
byte[] bytes = FileUtils.readFileToByteArray(file);
HttpHeaders headers = new HttpHeaders();
//设置响应内容类型
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
//设置内容描述
headers.setContentDispositionFormData("attachment",new String((filename+"."+ext).getBytes("utf-8"),"iso-8859-1"));
ResponseEntity result=new ResponseEntity(bytes,headers, HttpStatus.OK);
return result;
}
运行程序,在地址栏输入"/download/index/jsp"向服务器发送请求,便可以下载index.jsp文件。
因为这种方式将文件一次性读取为一个字节数组,所以只适用于文件较小时使用。