SpringBoot文件上傳(MVC狀況和webFlux狀況)

MVC狀況

引入依賴javascript

<?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框架自行下載)html

<!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代碼前端

這個是通用的返回結果vue

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

下面是核心上傳代碼(一個單文件上傳,一個多文件上傳)java

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.propertiesjquery

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

 

啓動項目:http://localhost:8080/ios

後臺打印:web

 

 WebFlux狀況(Spring5的新產品)

 這種狀況不一樣於SpringMVC,我只能以我目前開發的狀況來講明。咱們是先後端分離的項目,前端用vue+vuetify+VueX+Axios,大概思路就是搞一個按鈕,當change事件發生就執行上傳操做。ajax

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

..spring

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

 

我已經測試這種方式能夠行得通

相關文章
相關標籤/搜索