文章目录
文件上传和下载
一、 前端file.html:
前端表单要求:为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传</title>
</head>
<body>
<form action="/fileUpload" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>
<input type="submit" value=”上传文件”>
</form>
<div>
<a href="/download">点击下载</a>
</div>
</body>
</html>
注意:
- multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,且不会对字符编码。
- multipart/form-data会将表单数据以多段表单项的形式进行上传。
二、JavaWeb文件上传与下载
(1)文件上传
需要导入两个jar包:
- commons-fileupload-1.2.1.jar
- commons-io-1.4.jar
常用方法:
- ServletFileUpload类用于解析上传的数据。
- ServletFileUpload.isMultipartContent(HttpServletRequest request):判断当前数据是否是多段的格式
List<FileItem> parseRequest(HttpServletRequest request)
:解析上传的数据,返回多个表单项- FileItem.isFormField():判断表单项是否为文件
- FileItem.getFieldName():获得表单项的name属性值
- FileItem.getString():获得表单项的值
- FileItem.getName():获取上传的文件名
- FileItem.write(file):将表单项中的文件写入到file所指向的磁盘目录
//判断上传的数据是否为多段数据
if(ServletFileUpload.isMultipartContent(req)){
//创建FileItemFactory工厂实现类
FileItemFactory fileItemFactory = new DiskFileItemFactory();
//创建用于解析上传数据的工具类ServletFileUpload
ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
//解析上传数据,得到表单项FileItem
List<FileItem> list = servletFileUpload.parseRequest(req);
//循环判断每一个表单项,是普通类型还是上传的文件
for(FileItem fileItem : list){
if(fileItem.isFormField()){
//普通表单项
System.out.pritln(fileItem.getFiledName());
System.out.pritln(fileItem.getString("UTF-8"));
}else{
//上传的文件
System.out.pritln(fileItem.getFiledName());
System.out.pritln(fileItem.getName());
fileItem.write(new File("e:\\"+fileItem.getName()));
}
}
}
(2)文件下载
//需要下载的文件名
String downloadFileName = "1.jpg";
//读取要下载的文件内容
ServletContext servletContext = getServletContext();
//获取要下载的文件类型
String mimeType = servletContext,getMimeType("/file"+downloadFileName)
//告诉客户端返回的数据类型
resp.setContentType(mimeType);
//告诉客户端收到的数据是用于下载使用还是使用响应头
if(req.getHeader("User-Agent").content("Firefox")){
resp.setHeader("Content-Disposition","attachment;
fiename==?UTF-8?B?"+new BASE64Encoder().encode("中国.jpg","UTF-8")));
}else{
resp.setHeader("Content-Disposition","attachment;
fiename="+URLEncoder.encode("中国.jpg","UTF-8"));
}
InputStream resourceAsStream = servletContext.getResourceAsStream("/file"+downloadFileName);
//获取响应输出流
OutputStream outputStream = resp.getOutputStream();
//将下载的文件内容回传给客户端
IOUtils.copy(resourceAsStream,outputStream);
注意:
- Content-Disposition响应头:表示收到的数据怎么处理
- attachment:表示附件,下载时使用
- req.getHeader(“User-Agent”):根据浏览器的不同使用不同的编码,火狐使用BASE64Encoder,谷歌等使用URLencoder编码,主要用于解决中文乱码问题。
三、 spring文件上传与下载:
(1)MultipartResolver
- springMVC可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver。
- Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。
- Spring MVC使用Apache Commons FileUpload技术实现了一MultipartResolver实现类:CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖ApacheCommonsFileUpload的组件。
- 在SpringMVC中,处理文件上传需要导入依赖包
commons-io
包:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
使用依赖包commons-io
的情形:
public String fileUpload(@RequestParam("file") CommonsMultipartFile
file)
(2)配置MultipartResolver
<!--文件上传配置-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolve
r">
<!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,
默认为ISO-8859-1 -->
<property name="defaultEncoding" value="utf-8"/>
<!-- 上传文件大小上限,单位为字节(10485760=10M)-->
<property name="maxUploadSize" value="10485760"/>
<property name="maxInMemorySize" value="40960"/>
</bean>
注意:
- bena的id必须为:multipartResolver ,否则上传文件会报400的错误。
CommonsMultipartFile 的常用方法:
- String
getOriginalFilename()
:获取上传文件的原名,如test.html - InputStream
getInputStream()
:获取文件流 - void
transferTo(File dest)
:将上传文件保存到一个目录文件中
(3)I/O流的形式文件上传
@Controller
public class FileController {
@RequestMapping("/upload")
public String fileUpload(@RequestParam("file") CommonsMultipartFile
file , HttpServletRequest request) throws IOException {
//获取文件名
String uploadFileName = file.getOriginalFilename();
//如果文件名为空,直接回到首页!
if ("".equals(uploadFileName)){
return "redirect:/index.jsp";
}
System.out.println("上传文件名: "+uploadFileName);
//上传路径保存设置
String path =
request.getServletContext().getRealPath("/upload");
//如果路径不存在,创建一个
File realPath = new File(path);
if (!realPath.exists()){
realPath.mkdir();
}
System.out.println("上传文件保存地址:"+realPath);
InputStream is = file.getInputStream(); //文件输入流
OutputStream os = new FileOutputStream(new
File(realPath,uploadFileName)); //文件输出流
//读取写出
int len=0;
byte[] buffer = new byte[1024];
while ((len=is.read(buffer))!=-1){
os.write(buffer,0,len);
os.flush();
}
os.close();
is.close();
return "redirect:/index.jsp";
}
}
注意:
- @RequestParam(“file”) 将name=file控件得到的文件封装成CommonsMultipartFile 对象
- 批量上传CommonsMultipartFile则为数组即可
(4)使用transferTo上传文件
@RequestMapping("/upload2")
public String fileUpload2(@RequestParam("file") CommonsMultipartFile
file, HttpServletRequest request) throws IOException {
//上传路径保存设置
String path = request.getServletContext().getRealPath("/upload");
File realPath = new File(path);
if (!realPath.exists()){
realPath.mkdir();
}
//上传文件地址
System.out.println("上传文件保存地址:"+realPath);
file.transferTo(new File(realPath +"/"+
file.getOriginalFilename()));
return "redirect:/index.jsp";
}
(5)文件下载
-
设置response 响应头
-
读取文件-- InputStream
-
写出文件-- OutputStream
-
关闭流(先开后关)
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response ,HttpServletRequest
request) throws Exception{
//要下载的图片地址
String path = request.getServletContext().getRealPath("/upload");
String fileName = "基础语法.jpg";
//1、设置response 响应头
response.reset(); //设置页面不缓存,清空buffer
response.setCharacterEncoding("UTF-8"); //字符编码
response.setContentType("multipart/form-data"); //二进制传输数据
//设置响应头
response.setHeader("Content-Disposition",
"attachment;fileName="+URLEncoder.encode(fileName, "UTF-8"));
File file = new File(path,fileName);
//2、读取文件--输入流
InputStream input=new FileInputStream(file);
//3、写出文件--输出流
OutputStream out = response.getOutputStream();
byte[] buff =new byte[1024];
int index=0;
//4、执行写出操作
while((index= input.read(buff))!= -1){
out.write(buff, 0, index);
out.flush();
}
out.close();
input.close();
return null;
}
前端:
<a href="/download">点击下载</a>
四、springboot中的文件上传与下载:
(1)SpringBoot 中处理文件上传使用默认的StandardServletMultipartResolver
,相关的自动配置在MultipartAutoConfiguration
之中。我们可以使用spring.servlet.multipart
进行配置,当然也可以使用commons-io
包,不过需要将默认的方法排除掉:
spring:
autoconfigure:
exclude: org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration
(2)文件上传下载
- springboot上传文件可以直接使用
org.springframework.web.multipart.MultipartFile
。 - 文件上传时需要定义上传文件的地址以及文件名,从file中获取字节流,输出到指定的文件路径中。
- 文件下载时,获取下载文件的完整路径,推荐使用Buffer缓冲流的方式下载文件。
@RestController
public class TestController {
@PostMapping("/fileUpload")
public String fileUpload(HttpServletRequest req, HttpServletResponse resp,@RequestParam("file") MultipartFile file) {
if(file.isEmpty()){
return "未选择上传文件";
}
String originalFilename = file.getOriginalFilename();
String path = "G:/glp/"+originalFilename;
File dest = new File(path);
if(!dest.getParentFile().exists()){
dest.getParentFile().mkdir();
}
try{
file.transferTo(dest);
return "上传成功";
}catch (IOException e){
e.printStackTrace();
}
return "上传失败";
}
@GetMapping("/download")
public String fileDownLoad(HttpServletRequest req,HttpServletResponse resp){
File file = new File("G:/glp/Java研发工程师.pdf");
if(!file.exists()){
return "下载文件不存在";
}
try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));){
byte[] buff = new byte[1024];
OutputStream outputStream = resp.getOutputStream();
int len =0;
while ((len=bis.read(buff))!=-1){
outputStream.write(buff,0,len);
outputStream.flush();
}
}catch (IOException e){
e.printStackTrace();
return "下载失败";
}
return "下载成功";
}
}
注意:
这里采用了transferTo来上传文件。
五、BufferedInputStream
BufferedInputStream是一个带有缓冲区的输入流,通常使用它可以提高我们的读取效率,现在我们看下BufferedInputStream的实现原理:
- BufferedInputStream内部有一个缓冲区,默认大小为8M,每次调用read方法的时候,它首先尝试从缓冲区里读取数据,若读取失败(缓冲区无可读数据),则选择从物理数据源(譬如文件)读取新数据(这里会尝试尽可能读取多的字节)放入到缓冲区中,最后再将缓冲区中的内容部分或全部返回给用户.由于从缓冲区里读取数据远比直接从物理数据源(譬如文件)读取速度快,所以BufferedInputStream的效率很高!
- 不带缓冲的操作,每读一个字节就要写入一个字节,由于涉及磁盘的IO操作相比内存的操作要慢很多,所以不带缓冲的流效率很低。带缓冲的流,可以一次读很多字节,但不向磁盘中写入,只是先放到内存里。等凑够了缓冲区大小的时候一次性写入磁盘,这种方式可以减少磁盘操作次数,速度就会提高很多!这就是inputstream与bufferedinputstream的区别.
- BufferedInputStream与BufferedOutputStream分别是FilterInputStream类和FilterOutputStream类的子类,实现了装饰设计模式。