JavaWeb、Spring、SpringBoot实现文件上传和下载

文件上传和下载

一、 前端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类的子类,实现了装饰设计模式。

猜你喜欢

转载自blog.csdn.net/glpghz/article/details/108893963