小言_互联网的博客

SpringMVC(四)—— 文件上传和下载

448人阅读  评论(0)

一、文件上传

1.1 文件上传

​ 在 Web 开发中,使用应用层协议 HTTP,通过在请求头中设置传输的内容类型 Content-Type 为 multipart/form-data; boundary=流分隔符值 来上传文件,这个流分隔符用来区分一个文件上传的开始和结束。

<%--   文件上传 表单提交的方式--%>
<%-- 文件上传的规范:enctype="multipart/form-data" method="post"--%>
<form enctype="multipart/form-data" action="upload" method="post">
    选择上传的文件:<input type="file" name="file">
    <input type="submit" value="点击上传" id="btn1">
</form>

表单中的enctype 属性说明:

  • application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
  • text/plain:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。

1.2 文件下载

通过在响应消息头中设置 Content-Disposition 和 Content-Type 使得浏览器无法使用某种方式或者激活某个程序来处理 MIME 类型的文件,来让浏览器提示是否保存文件,也就是文件的下载。

response.setHeader("content-disposition","attachment;filename="+filename);
response.setContentType("application/octet-stream");

二、SpringMVC中的文件上传与下载

前提:自己在本机配置一个虚拟目录

可以在tomcat的conf目录下面的server.xml中设置:

在文件的Host标签中设置虚拟目录:

<Context  path="/updatefile"  docBase="E:\tomcat_test_uploadAndupdown" reloadable="true"/>

属性说明:

代码说明
元素,用于将本地文件系统中的一个目录,映射成一个可供Web浏览器访问的虚拟根目录

path属性 用于指定Web应用的虚拟路径
docBase属性 用于指定该虚拟路径,所映射到的本地文件系统目录
reloadable 如果设置为true,则tomcat服务器在运行时,会监视WEB-INF/classes和WEB-INF/lib目录下类的改变

详细可参考:

2.1 文件表单上传

1.配置文件上传解析器,限制上传文件的大小
 <bean id="multipartResolver" class="com.gx.CommonsMultipartResolverPlus.PlusCommonsMultipartResolver">
</bean>

这个拦截器可以对上传文件进行一些限定:比如设置编码,设置文件上传的大小

	 <!--设置请求编码-->
	 <property name="defaultEncoding" value="UTF-8"/>
    <property name="uploadTempDir" value="WEB-INF/tmp"/>
    <!--设置允许单个上传文件的最大值,不要在这里配置-->
    <!--<property name="maxUploadSizePerFile" value="31457280"/>-->
    <!--延迟解析,在Controller中抛出异常-->
	<property name="resolveLazily" value="true"/>

当文件上传大小出现异常的时候,设置resolveLazily属性为true,在拦截器中对文件上传进行限制,拦截器抛出异常,在Controller中获取异常,进行处理。

拦截器限制文件的大小(这里只写了一个preHandle方法):

public class FileUploadInterceptor implements HandlerInterceptor {
    private long maxSize;

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        if (httpServletRequest != null && ServletFileUpload.isMultipartContent(httpServletRequest)) {
            ServletRequestContext servletRequestContext = new ServletRequestContext(httpServletRequest);
            long requestSize = servletRequestContext.contentLength();
            if (requestSize > maxSize) {
                // 抛出异常
                throw new MaxUploadSizeExceededException(maxSize);
            }
        }
        return true;
    }
   public void setMaxSize(long maxSize) {
        this.maxSize = maxSize;
    }
}

Controller处理异常

@ExceptionHandler(MaxUploadSizeExceededException.class)
public String handException(MaxUploadSizeExceededException e, HttpServletRequest request) {
    request.setAttribute("msg", "文件超过了指定大小,上传失败!");
    return "fileupload";
}	
2.表格实现文件上传
<form enctype="multipart/form-data" action="upload" method="post">
    选择上传的文件:<input type="file" name="file">
    <input type="submit" value="点击上传" id="btn1">
</form>
<script>
    $(function () {
        $("#btn1").click(function(){
            var tt=  setInterval(function(){
                $.ajax({
                    type: "get",
                    url: "uploadState",
                    success: function (result) {
                        console.log(result);
                        $("#uploadFileState").text(result.msg);
                        if (result.msg=="ok"){
                            clearInterval(tt)
                        }
                    }
                });

            },1000);
        });
</script>
3.后台controller
//配置文件上传的路径
public String TargetPath="E:\\tomcat_test_uploadAndupdown";
@RequestMapping(value = "/uploadState",method = RequestMethod.POST)
    public String upload(@RequestParam("uploadFile") CommonsMultipartFile uploadFile) throws IOException {
        //获取文件在客户端真实名字
        String fname =  uploadFile.getOriginalFilename();
       ////如果出现文件名称相同的话,我们需要将存储的文件名称唯一化。加入UUID保证文件名唯一
        String uuidName= UUID.randomUUID().toString();
        File TargetFile = new File(TargetPath+File.separator+FileRealName);
        //将文件储存在服务器的位置
        uploadFile.transferTo(TargetFile);
        return "index";
    } 

过程:

前台表单提交之后,springMVC识别多媒体文件之后,会将表单内容自动注入到 CommonsMultipartFile中。我们可以通过getOriginalFilename()方法获取到文件的名称,为了防止文件名重名,使用UUID对文件名进行处理。然后根据我们自己的需求,将文件上传到一个文件夹或者是一个服务器的上面。

2.2 Ajax实现上传文件

Ajax实现文件上传的优点:页面无刷新上传

1.可以参考上面配置文件上传解析器,限制文件大小
2. 发送Ajax实现文件上传
<input type="file" name="file" id="file" >
<input type="submit" id="btn2" value="上传文件">
 <script>  
    $("#btn2").click(function () {
            //取表单的数据
            var formData=new FormData();
            formData.append("file",$("#file")[0].files[0]);
            alert(formData)
           $.ajax({
               url:"uploadAjax",//文件返回一个ajax
               type:"post",
               data:formData,
               contentType:false,//大小写错误的话报400  
               //Required CommonsMultipartFile parameter 'xxx' is not present
               processData:false,
               success:function (result) {
                   console.log(result.msg);
               }
           })
        });
   </script>
3.Controller
@RequestMapping(value = "/uploadAjax",method = RequestMethod.POST)
    @ResponseBody
    public Map uploadAjax(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
        //获取上传文件的真实名字
        String FileRealName = file.getOriginalFilename();
        String uuidName = UUID.randomUUID().toString();//保证名称唯一
        //传递到当前项目的目录下
        File TargetFile = new File(TargetPath+File.separator+FileRealName);
        //文件存储的位置
        file.transferTo(TargetFile);
        Map<String, String> msp = new HashMap<>();
        msp.put("msg","文件上传完毕");
        System.out.println(TargetFile.getAbsolutePath());
        return msp;
    }

2.3 多文件上传

多文件表单上传:
<form enctype="multipart/form-data" action="uploadMore" 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 = "/uploadMore",method = RequestMethod.POST)
    public String uploadMore(@RequestParam("file") CommonsMultipartFile[] files, HttpServletRequest request)   {
        try {
            for (CommonsMultipartFile file : files) {
                //获取上传文件的真实名字
                String FileRealName = file.getOriginalFilename();
                //传递到当前项目的目录下
                File TargetFile = new File(TargetPath+File.separator+FileRealName);
                //文件存储的位置
                file.transferTo(TargetFile);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        }
        return "index";
    }

2.4 文件下载

文件上传下载设置返回值为ResponseEntity

@RequestMapping("/downLoadFile/{fileName:.+}")
    public ResponseEntity downLoadFile(@PathVariable("fileName")String fileName, HttpServletRequest request) throws IOException {
        System.out.println(fileName);
        File file = new File(TargetPath + File.separator + fileName);
        //处理中文
        String agent = request.getHeader("user-agent");
        fileName = DownLoadUtils.getFileName(agent, fileName);
        //读取文件
        byte[] bytes = FileUtils.readFileToByteArray(file);
        HttpHeaders httpHeaders = new HttpHeaders();
        //设置响应头
        httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        // fileName = new String(fileName.getBytes("UTF-8"), "iso8859-1");
        httpHeaders.setContentDispositionFormData("attachment",fileName);
        ResponseEntity responseEntity = new ResponseEntity(bytes,httpHeaders, HttpStatus.OK);
        System.out.println("文件下载成功");
        return responseEntity;
    }

根据前端url中获取到需要下载的文件名。文件下载最重要的是设置响应头。通过httpHeaders进行设置

 httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
 httpHeaders.setContentDispositionFormData("attachment",fileName);

也可以通过response进行相应头的设置

 response.setContentType("application/octet-stream");
response.setHeader("content-disposition","attachment;filename="+fileName);

处理中文的问题

1.编写工具类,上面代码所使用的DownLoadUtils,根据浏览器的请求头中user-agent属性进行编码的设置

public class DownLoadUtils {

    public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
        if (agent.contains("MSIE")) {
            // IE浏览器
            filename = URLEncoder.encode(filename, "utf-8");
            filename = filename.replace("+", " ");
        } else if (agent.contains("Firefox")) {
            // 火狐浏览器
            BASE64Encoder base64Encoder = new BASE64Encoder();
            filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
        } else {
            // 其它浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        }
        return filename;
    }
}

调用方法,传入agent以及文件名。

  1. 使用Sting的一个构造方法,对编码进行设置

        public String(byte bytes[], String charsetName)
                throws UnsupportedEncodingException {
            this(bytes, 0, bytes.length, charsetName);
        }
    
headers.setContentDispositionFormData("attachment",new String((filename).getBytes("utf-8"),"iso-8859-1"));
ResponseEntity

ResponseEntity可以标识整个http相应,包括状态码、头部信息以及响应体内容

  1. 可以使用任意类型作为响应体;
  2. 可以通过编程方式指明响应状态,根据不同场景返回不同状态;
  3. 设置http响应头;
ResponseEntity responseEntity = new ResponseEntity(bytes,httpHeaders, HttpStatus.OK);

状态码数据头信息


转载:https://blog.csdn.net/weixin_45707030/article/details/106577643
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场