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;
        }

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

  • 相关阅读:
    智能算法浅介模拟退火,遗传算法,禁忌搜索,神经网络等
    shell 字符串操作(长度,查找,替换)详解
    如何实现两个文件相减的功能(剔除)
    shell数值操作(四则运算,浮点数,科学计数法)awk,bc
    CUDA程序优化的记录
    随机化算法模拟退火
    (转)AspNetPager 样式以及使用(漂亮)
    (转)做项目时,如何做比较美观大方的数据输入窗体
    (转)26个Jquery使用小技巧(jQuery tips, tricks & solutions)
    (转)【译】Asp.net MVC并不仅仅只是Linq to SQL
  • 原文地址:https://www.cnblogs.com/LUA123/p/10518272.html
Copyright © 2011-2022 走看看