SpringMVC文件上传下载

上传

上传文件最常见的就是通过form表单提交,但是注意需要添加enctype="multipart/form-data"

<%--action如果加“/”就表示从上下文开始,需要加request.getContextPath()--%>
<form name="FirstForm" action="fileUpload.do" method="post" enctype="multipart/form-data">
    <h1>采用流的方式上传文件</h1>
    <input type="file" name="file">
    <input type="submit" value="upload"/>
</form>

不要以为只是在请求头中添加Content-Type:multipart/form-data,它还有一个分隔符boundary,用于分隔多个上传部分,一般情况下浏览器会自动维护添加,但也有不会添加的时候,比如angular中使用的HttpClient发送的post请求,如果只设置内容格式为“multipart/form-data”就会报没有分割多部分标识异常。

用SpringMVC作为后端框架处理:
1.在对应DispatcherServlet的配置文件中添加

<!-- SpringMVC上传文件时,需要配置MultipartResolver处理器 -->
    <bean id="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="UTF-8" />
        <!-- 指定所上传文件的总大小不能超过200KB。注意maxUploadSize属性的限制不是针对单个文件,而是所有文件的容量之和 -->
        <property name="maxUploadSize" value="104857600" /><!-- -1表示无限制,单位byte -->
    </bean>

    <!-- SpringMVC在超出上传文件限制时,会抛出org.springframework.web.multipart.MaxUploadSizeExceededException -->
    <!-- 该异常是SpringMVC在检查上传的文件信息时抛出来的,而且此时还没有进入到Controller方法中 -->
    <bean id="exceptionResolver"
          class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <!-- 遇到MaxUploadSizeExceededException异常时,自动跳转到指定的页面,如果配置了视图会按视图设置加前后缀 -->
                <prop
                        key="org.springframework.web.multipart.MaxUploadSizeExceededException">exception</prop>
            </props>
        </property>
    </bean>

2.处理器方法:

    /*
     * 采用transferTo来保存上传的文件,使用缓存流
     * @RequestParam("file")用于接收form中name为file的文件输入
     * SpringMVC把文件封装成CommonsMultipartFile对象,且必须指定
     */
    @RequestMapping("fileUpload")
    public String fileUpload(@RequestParam("file") CommonsMultipartFile file) throws IOException {
        String path = desktopPath + file.getOriginalFilename();
        File newFile = new File(path);
        file.transferTo(newFile);
        return "welcome";
    }

即使没有上传的文件,这个请求也不会报异常,只是这个file.getOriginalFilename为“”。
如果上传的表单中有多个name为file的上传文件,那么此种方法只能获取第一个name为file的文件,注意。
为解决这个问题可以用CommonsMultipartFile数组接收,如public String fileUpload(@RequestParam("file") CommonsMultipartFile[] files)

CommonsMultipartFile还有其他几种用法,如下演示:

@RequestMapping("springUpload")
    public String springUpload(HttpServletRequest request) throws IllegalStateException, IOException {
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(
                request.getSession().getServletContext());
        //检查请求中Content-Type以multipart/开头,且是post请求
        if (multipartResolver.isMultipart(request)) {
            MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
            //获取multiRequest中所有文件的请求名称,非真实文件名
            Iterator iter = multiRequest.getFileNames();
            while (iter.hasNext()) {
                MultipartFile file = multiRequest.getFile(iter.next().toString());
                if (file != null) {
                    String path = desktopPath + file.getOriginalFilename();
                    //上传
                    file.transferTo(new File(path));
                }
            }
        }
        return "welcome";
    }
    @RequestMapping("ajaxUpload")
    public String ajaxUpload(HttpServletRequest request) throws IOException {
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(
                request.getSession().getServletContext());
        if (multipartResolver.isMultipart(request)) {
            MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;
            Map<String, MultipartFile> map = multipartHttpServletRequest.getFileMap();
            Collection<MultipartFile> multipartFiles = map.values();
            for (MultipartFile multipartFile : multipartFiles) {
                String fileName = multipartFile.getOriginalFilename(); //文件名,如果没传文件为""
                String fieldName = multipartFile.getName(); //表单name字段名
                if ("".equals(fileName)) continue;
                String path = desktopPath + fileName;
                multipartFile.transferTo(new File(path));
            }
        }
        return "welcome";
    }

那么是否只有通过form表单的形式才能上传文件呢?肯定不是,一般的ajax请求也可以完成。
1.

<form id="uploadForm">
    <h1>通过ajax上传文件</h1>
    <input type="file" name="file1">
    <input type="file" name="file2">
    <button onclick="doUpload()">upload</button>
</form>

对应的js代码:

        function doUpload() {
            var formData = new FormData($("#uploadForm")[0]);
            $.ajax({
            	//写这个域名是为了解决Charles无法抓取localhost,其实就是localhost
                url: 'http://localhost.charlesproxy.com:8080/learn/ajaxUpload.do',	
                type: 'post',
                data: formData,
                async: true,
                contentType: false,
                processData: false,
                success: function (res) {
                    console.log(res);
                }
            });
        }

这里我没有给表单设置ContentType,后端同样可以获取到数据,说明只是通过Form表单的action方式提交需要设置。

<div>
    <h1>文件数组</h1>
    <input type="file" id="file1">
    <input type="file" id="file2">
    <input type="button" value="upload" onclick="doUpload2()">
</div>

对应的js代码:

function doUpload2(){
            var obj = new FormData();
            var file1 = document.getElementById("file1").files[0];
            var file2 = document.getElementById("file2").files[0];
            obj.append("file", file1);
            obj.append("file", file2);
            $.ajax({
                url: 'http://localhost.charlesproxy.com:8080/learn/ajaxUpload2.do',
                type: 'post',
                data: obj,
                async: true,
                contentType: false,
                processData: false,
                success: function (res) {
                    console.log(res);
                }
            });
        }

自定义请求报文上传

之前写的几个都是浏览器生成的请求报文上传的,我们也可以自定义请求报文,那就必须先熟悉报文的内容。
这是我用Charles截取的报文内容,图片的具体数据我删除了。
在这里插入图片描述
请求头的参数说明可以查看

https://blog.csdn.net/qq_30553235/article/details/79282113

只上传一个文件的报文设置:

    @RequestMapping("typeDesc")
    public String typeDesc(String fileName) {
        try {
            fileName = URLDecoder.decode(fileName, encode);
            //换行符
            String newLine = "\r\n";
            //分割线前缀
            String boundaryPrefix = "--";
            //数据分割线
            String boundary = "WebKitFormBoundaryEE43EOKHrgdYmN1k";
            URL url = new URL("http://localhost.charlesproxy.com:8080/learn/ajaxUpload2.do");
            // 此时uconn只是一个连接对象,待连接中
            URLConnection uconn = url.openConnection();
            if (uconn instanceof HttpURLConnection) {
                HttpURLConnection conn = (HttpURLConnection) uconn;
                //------------------------设置请求头-----------------------------------
                //设置请求方式为POST,需要为大写
                conn.setRequestMethod("POST");
                //设置连接输出流为true,默认false (post请求是以流的方式隐式的传递参数)
                conn.setDoOutput(true);
                //设置连接输入流为true
                conn.setDoInput(true);
                conn.setUseCaches(false);
                //持久连接,HTTP/1.1的默认设置,如果需要关闭可以加入 Connection: close
                conn.setRequestProperty("Connection", "keep-alive");
                conn.setRequestProperty("Accept-Charset", "UTF-8");
                conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
                //建立连接,但请求未开始,以上各个参数设置需在此方法之前进行。当conn.getInputStream()方法调用时才发起请求
                conn.connect();
                //------------------------按格式设置请求数据-----------------------------------
                // 上传文件
                File file = new File(desktopPath + fileName);
                StringBuilder sb = new StringBuilder();
                sb.append(boundaryPrefix);
                sb.append(boundary);
                sb.append(newLine);
                // file是传的字段名
                sb.append("Content-Disposition: form-data;name=\"file\";filename=\"" + fileName + "\"" + newLine);
                sb.append("Content-Type:application/octet-stream");
                // 参数头设置完以后需要两个换行,然后才是参数内容
                sb.append(newLine);
                sb.append(newLine);

                OutputStream out = new DataOutputStream(conn.getOutputStream());
                // 将参数头的数据写入到输出流中
                out.write(sb.toString().getBytes());
                // 数据输入流,用于读取文件数据
                DataInputStream in = new DataInputStream(new FileInputStream(file));
                byte[] bufferOut = new byte[1024];
                int bytes = 0;
                // 每次读1KB数据,并且将文件数据写入到输出流中
                while ((bytes = in.read(bufferOut)) != -1) {
                    out.write(bufferOut, 0, bytes);
                }
                // 最后添加换行
                out.write(newLine.getBytes());
                in.close();
                // 定义最后数据分隔线,即--加上BOUNDARY再加上--。
                byte[] end_data = (boundaryPrefix + boundary + boundaryPrefix + newLine)
                        .getBytes();
                // 写上结尾标识
                out.write(end_data);
                out.flush();
                out.close();
                //-----------------------请求设置完成并关闭流----------------------------

                //--------调用conn.getInputStream()连接发起请求,处理服务器响应------------
                //定义BufferedReader输入流来读取URL的响应
                BufferedReader reader = new BufferedReader(new InputStreamReader(
                        conn.getInputStream()));
                String line = null;
                while ((line = reader.readLine()) != null) {
                    System.out.println(line);
                }
                reader.close();
                conn.disconnect();
                //----------------关闭流并销毁连接-------------------
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "welcome";
    }

其他的几种请求也类似,可以先查看报文在构造。

下载

传统Java下载可以有两种,一种是浏览器可以直接预览,一种是另存为下载。下面看演示:

/**
     *
     * @param request 当前请求
     * @param response  当前响应
     * @param isOnline 在线预览
     */
    @RequestMapping("download")
    public void download(HttpServletRequest request, HttpServletResponse response, boolean isOnline) {
        BufferedInputStream dis = null;
        BufferedOutputStream fos = null;

        String urlString = "https://cdn.duitang.com/uploads/item/201205/03/20120503103536_uUWSi.jpeg";
        String fileName = urlString.substring(urlString.lastIndexOf('/') + 1);
        response.reset();
        try {
            URL url = new URL(urlString);
            URLConnection conn = url.openConnection();
            if (isOnline) {
                response.setContentType(conn.getContentType()); //隐式执行conn.connect();
                response.setHeader("Content-Disposition", "inline;filename=" + fileName);	//预览
            } else {
                response.setContentType("application/octet-stream");
                response.setHeader("Content-disposition", "attachment; filename=" + fileName);	//下载
                response.setHeader("Content-Length", String.valueOf(conn.getContentLength())); //隐式执行conn.connect();
            }

            dis = new BufferedInputStream(conn.getInputStream());
            fos = new BufferedOutputStream(response.getOutputStream());

            byte[] buff = new byte[2048];
            int bytesRead;
            while (-1 != (bytesRead = dis.read(buff, 0, buff.length))) {
                fos.write(buff, 0, bytesRead);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (dis != null)
                try {
                    dis.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            if (fos != null)
                try {
                    fos.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
        }
    }

SpringMVC下载方式:

    @RequestMapping("/downloadSpringMVC")
    public ResponseEntity<byte[]> export(String fileName) throws IOException {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentDispositionFormData("attachment", fileName);
        File file = new File(desktopPath + fileName);
        return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED);
    }

页面显示图片

上传的图片如果不需要保存到后台,只是页面上显示一下,可以这样做:

<div>
    <h1>页面显示图片</h1>
    <input type="file" name="file" onchange="showPreview(this)">
    <img id="img">
</div>

js代码:

function showPreview(source) {
            var file = source.files[0];
            if (window.FileReader) {
                var fr = new FileReader();
                fr.onloadend = function (e) {
                    document.getElementById("img").src = e.target.result;
                };
                fr.readAsDataURL(file);
            }
        }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值