zoukankan      html  css  js  c++  java
  • SpringBoot文件上传(MVC情况和webFlux情况)

    MVC情况

    引入依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.3.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.example</groupId>
        <artifactId>demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>demo</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>2.6</version>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>

    前台(两个js框架自行下载)

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Index</title>
        <style>
            html, body {
                margin: 0;
                padding: 0;
                height: 100%;
                min-height: 100%;
            }
    
            .header {
                padding: 1px;
                position: relative;
                left: 0;
                top: 0;
                width: 100%;
                height: 70px;
                background-color: #4E3384;
                color: #c7acff;
            }
    
            .header h2 {
                text-align: center;
            }
    
            .header a {
                display: block;
                position: absolute;
                top: 18px;
                right: 15px;
                padding: 8px 15px;
                background-color: #a27bf1;
                color: #fff;
                border-radius: 3px;
                text-decoration: none;
            }
    
            .container {
                min-height: 100%;
            }
    
            .main {
                max-width: 1200px;
                margin: 30px auto;
                text-align: center;
            }
    
            .file-wrap {
                position: relative;
                padding: 8px 10px;
                background-color: #ad0660;
                color: #fff;
                text-decoration: none;
                font-size: 14px;
                border-radius: 3px;
                margin: 60px 25px;
                display: inline-block;
            }
    
            .file-wrap:hover {
                background-color: #d80b7a;
            }
    
            .file-input {
                font-size: 0;
                position: absolute;
                left: 0;
                top: 0;
                width: 100%;
                height: 100%;
                cursor: pointer;
                opacity: 0;
            }
        </style>
    </head>
    <body>
    <div class="container">
        <div class="header">
            <h2>文件上传</h2>
        </div>
        <div class="main">
            <a href="javascript:;" class="file-wrap">单文件上传
                <input type="file" id="singleFile" name="singleFile" class="file-input">
            </a>
            <a href="javascript:;" class="file-wrap">多文件上传
                <input type="file" id="multiFile" name="multiFile" class="file-input" multiple>
            </a>
            <div id="imgDiv"></div>
        </div>
    </div>
    <script th:src="@{js/jquery-3.3.1.min.js}"></script>
    <script th:src="@{js/ajaxfileupload.js}"></script>
    <script>
    
        $(document).on('change', '#singleFile', function () {
            $.ajaxFileUpload({
                url: '/upload/single', // 用于文件上传的服务器端请求地址
                secureuri: false, // 是否需要安全协议,一般设置为false
                fileElementId: 'singleFile', // 文件上传域的ID
                dataType: 'json', // 返回值类型 一般设置为json
                // 服务器成功响应处理函数
                success: function (data, status) {
                    alert(data.msg);
                    if (data.code == 1){
                        $('#imgDiv').append($('<img src="'+ data.data +'">'));
                    }
                },
                // 服务器响应失败处理函数
                error: function (data, status, e) {
                    alert(e);
                }
            });
            $('#singleFile').val('');
        })
    
        $(document).on('change', '#multiFile', function () {
            $.ajaxFileUpload({
                url: '/upload/multi', // 用于文件上传的服务器端请求地址
                secureuri: false, // 是否需要安全协议,一般设置为false
                fileElementId: 'multiFile', // 文件上传域的ID
                dataType: 'json', // 返回值类型 一般设置为json
                // 服务器成功响应处理函数
                success: function (data, status) {
                    alert(data.msg);
                    if (data.code == 1){
                        for (var i = 0; i < data.data.length; i++){
                            $('#imgDiv').append($('<img src="'+ data.data[i] +'">'));
                        }
                    }
                },
                // 服务器响应失败处理函数
                error: function (data, status, e) {
                    alert(e);
                }
            });
            $('#multiFile').val('');
        })
    
    </script>
    </body>
    </html>

    最后是Java代码

    这个是通用的返回结果

    package com.example.demo;
    
    import lombok.Data;
    
    @Data
    public class BaseResponse<T> {
    
        private T data;
        private int code = 1;   // 0-false;1-true;默认1
        private String msg = "success";
    }

    下面是核心上传代码(一个单文件上传,一个多文件上传)

    package com.example.demo;
    
    import org.apache.commons.io.FileUtils;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    @RestController
    @RequestMapping("/upload")
    public class FileController {
    
        @PostMapping("/single")
        public BaseResponse<String> single(@RequestParam("singleFile") MultipartFile file, HttpServletRequest req) throws IOException {
            String fileName = file.getOriginalFilename();
            String fileType = fileName.substring(fileName.lastIndexOf("."));
            String newFileName = new Date().getTime() + "";
            String fileSize = FileUtils.byteCountToDisplaySize(file.getSize());
            System.out.println("文件名:" + fileName);
            System.out.println("文件大小:" + fileSize);
            String path = req.getServletContext().getRealPath("/MyFiles/"); // 保存在项目运行目录下的MyFiles文件夹
            File targetFile = new File(path + newFileName + fileType);
            FileUtils.copyInputStreamToFile(file.getInputStream(), targetFile);
            String imgPath = targetFile.getPath();
            System.out.println("保存路径:" + imgPath);
    //        String url = req.getScheme() + "://" + req.getServerName() + req.getContextPath() +
    //                "/MyFiles/" + newFileName + fileType;
            String url = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + req.getContextPath() +
                    "/MyFiles/" + newFileName + fileType;
            System.out.println("URL:" + url);
            BaseResponse<String> response = new BaseResponse<>();
            response.setData(url);
            return response;
        }
    
        @PostMapping("/multi")
        public BaseResponse<List<String>> multi(@RequestParam("multiFile") MultipartFile[] files, HttpServletRequest req) throws IOException {
            List<String> urls = new ArrayList<>();
            for (MultipartFile file : files){
                String fileName = file.getOriginalFilename();
                String fileType = fileName.substring(fileName.lastIndexOf("."));
                String newFileName = new Date().getTime() + "";
                String fileSize = FileUtils.byteCountToDisplaySize(file.getSize());
                System.out.println("文件名:" + fileName);
                System.out.println("文件大小:" + fileSize);
                String path = req.getServletContext().getRealPath("/MyFiles/");
                File targetFile = new File(path + newFileName + fileType);
                FileUtils.copyInputStreamToFile(file.getInputStream(), targetFile);
                String imgPath = targetFile.getPath();
                System.out.println("保存路径:" + imgPath);
                String url = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + req.getContextPath() +
                        "/MyFiles/" + newFileName + fileType;
                System.out.println("URL:" + url);
                urls.add(url);
                System.out.println("=======================================");
            }
    
            BaseResponse<List<String>> response = new BaseResponse<>();
            response.setData(urls);
            return response;
        }
    }

     最后你可以配置上传文件大小,在application.properties

    spring.servlet.multipart.max-file-size=5MB
    spring.servlet.multipart.max-request-size=10MB

    启动项目:http://localhost:8080/

    后台打印:

     WebFlux情况(Spring5的新产品)

     这种情况不同于SpringMVC,我只能以我目前开发的情况来说明。我们是前后端分离的项目,前端用vue+vuetify+VueX+Axios,大概思路就是搞一个按钮,当change事件发生就执行上传操作。

    <v-btn dark small color="blue darken-1">上传文件
        <input type="file" id="pbFileInput" class="file-input" @change="uploadFile('pbFileInput')"/>
    </v-btn>

    ..

    // id 为文件域的id
    uploadFile: function(id){
        let me = this;
        let formData = new window.FormData();
        formData.append('file',document.querySelector('#'+id).files[0])
        let options = {  // 设置axios的参数
            headers: {
                'Content-Type': 'multipart/form-data'
            }
        }
        me.$store.state.axios.post('/upload',formData, options)
            .then(function (response) {
                let data = response.data;
                if (data.code == 0){
                    console.log(data);
                    document.querySelector('#'+id).value = '';  // 解决上传第二次不能选择同一文件
                } else{
                    console.log(data.msg)
                }
            })
            .catch(function (error) {
                console.log(error);
            });
    
    }

    后端(这个是借鉴网友的代码,还可以用)

        private static final String BASE_PATH = "/MyFiles/";
    
        @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
        public Mono<BaseResponse<String>> requestBodyFlux(@RequestPart("file") FilePart filePart) throws IOException {
            String base = BASE_PATH;  // 存放在当前磁盘的根目录
            System.out.println(filePart.filename());
            Path path = Paths.get(base);
            if (!Files.exists(path)){
                Files.createDirectories(path);
            }
            Path file = Files.createFile(Paths.get(base + filePart.filename()));
    
            // 方法一
            AsynchronousFileChannel channel =
                    AsynchronousFileChannel.open(file, StandardOpenOption.WRITE);
            DataBufferUtils.write(filePart.content(), channel, 0)
                    .doOnComplete(() -> {
                        System.out.println("finish");
                    })
                    .subscribe();
    
            // 方法二
    //        filePart.transferTo(file.toFile());
    
            System.out.println(file.toString());
    
            BaseResponse<String> response = new BaseResponse<>();
            response.setData(filePart.filename()); // 把文件名传回给前端
            return Mono.just(response);
        }

     填坑:网友的代码也不是万能的哦

        @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
        public Mono<BaseResponse<String>> requestBodyFlux(@RequestPart("file") FilePart filePart, @RequestHeader("uniqueId") String uniqueId) throws IOException {
            String base = baseConfiguration.getPbUploadPath();  // 存放在当前磁盘的根目录
            if (StringUtils.isEmpty(uniqueId)){
                uniqueId = randomNumber();   // 每个pb协议有个独立的文件夹名称
            }
            String filename = filePart.filename();
            log.info("=======================上传文件=======================");
            log.info(filename);
            log.info(uniqueId);
            Path path = Paths.get(org.apache.commons.lang3.StringUtils.appendIfMissing(base, "/") + uniqueId + "/");
            if (!Files.exists(path)){
                Files.createDirectories(path);
            }
            // 如果存在同名文件,先删除
            Path targetPath = Paths.get(org.apache.commons.lang3.StringUtils.appendIfMissing(base, "/") + uniqueId + "/" + filename);
            if (Files.exists(targetPath)){
                boolean b = Files.deleteIfExists(targetPath);
                log.info("已存在同名文件:" + filename + ",先删除:" + b);
            }
            // 再建立新的
            Path tempFile = Files.createFile(targetPath);
    
            // 方法一
            AsynchronousFileChannel channel =
                    AsynchronousFileChannel.open(tempFile, StandardOpenOption.WRITE);
            DataBufferUtils.write(filePart.content(), channel, 0)
                    .doOnComplete(() -> {
                        log.info("文件写入完毕...");
                        // 不关闭的话如果再上传同一个文件,会报错:java.nio.file.AccessDeniedException,因为资源被占用,无法删除
                        log.info("文件流关闭...");
                        try {
                            channel.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                            log.info("文件流关闭失败...");
                        }
                    })
                    .subscribe();
    
            // 方法二
    //        filePart.transferTo(tempFile.toFile());
    
            log.info(tempFile.toString());
            log.info("=======================--------=======================");
    
            BaseResponse<String> response = new BaseResponse<>();
            response.setData(filename + "," + uniqueId); // 把唯一id和文件名传回给前端
            return Mono.just(response);
        }

    生成随机文件夹名字

        private String randomNumber(){
            long time = new Date().getTime();
            String s = time + "";
            Random random = new Random();
            for (int i = 0; i < 4; i++){
                s += random.nextInt(10);
            }
            return s;
        }

    我已经测试这种方式可以行得通

  • 相关阅读:
    【Azure 应用服务】由 Azure Functions runtime is unreachable 的错误消息推导出 ASYNC(异步)和 SYNC(同步)混用而引起ThreadPool耗尽问题
    【Azure API 管理】是否可以将Swagger 的API定义导入导Azure API Management中
    【Azure 应用服务】Azure Function 不能被触发
    【Azure 环境】Azure Key Vault (密钥保管库)中所保管的Keys, Secrets,Certificates是否可以实现数据粒度的权限控制呢?
    【Azure 事件中心】为应用程序网关(Application Gateway with WAF) 配置诊断日志,发送到事件中心
    【Azure 事件中心】azure-spring-cloud-stream-binder-eventhubs客户端组件问题, 实践消息非顺序可达
    【Azure API 管理】Azure API Management通过请求中的Path来限定其被访问的频率(如1秒一次)
    【Azure 环境】前端Web通过Azure AD获取Token时发生跨域问题(CORS Error)
    【Azure 应用服务】记一次Azure Spring Cloud 的部署错误 (az spring-cloud app deploy -g dev -s testdemo -n demo -p ./hellospring-0.0.1-SNAPSHOT.jar --->>> Failed to wait for deployment instances to be ready)
    【Azure 应用服务】App Service中抓取 Web Job 的 DUMP 办法
  • 原文地址:https://www.cnblogs.com/LUA123/p/10518272.html
Copyright © 2011-2022 走看看