springboot2.x文件上傳

pom包的配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

啓動項類修改

/**
  * 防止文件大於10M時Tomcat鏈接重置
  *
  * @return
  */
@Bean
public TomcatServletWebServerFactory tomcatEmbedded() {
    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
    tomcat.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> {
        if ((connector.getProtocolHandler() instanceof AbstractHttp11Protocol<?>)) {
            ((AbstractHttp11Protocol<?>) connector.getProtocolHandler()).setMaxSwallowSize(-1);
        }
    });
    return tomcat;
}

配置文件修改

# 禁用 thymeleaf 緩存
spring.thymeleaf.cache=false
# 是否支持批量上傳   (默認值 true)
spring.servlet.multipart.enabled=true
# 上傳文件的臨時目錄 (通常狀況下不用特地修改)
spring.servlet.multipart.location=
# 上傳文件最大爲 1M (默認值 1M 根據自身業務自行控制便可)
spring.servlet.multipart.max-file-size=10MB
# 上傳請求最大爲 10M(默認值10M 根據自身業務自行控制便可)
spring.servlet.multipart.max-request-size=10MB
# 文件大小閾值,當大於這個閾值時將寫入到磁盤,不然存在內存中,(默認值0 通常狀況下不用特地修改)
spring.servlet.multipart.file-size-threshold=0
# 判斷是否要延遲解析文件(至關於懶加載,通常狀況下不用特地修改)
spring.servlet.multipart.resolve-lazily=false

file.upload.path: /file/upload

單文件上傳

@PostMapping("/upload")
public Map<String, String> upload(@RequestParam MultipartFile file) throws IOException {
    //建立本地文件
    File localFile = new File(path, file.getOriginalFilename());
    //把傳上來的文件寫到本地文件
    file.transferTo(localFile);
    //返回localFile文件路徑
    Map<String, String> path = new HashMap<>();
    path.put("path", localFile.getAbsolutePath());
    return path;
}

這時候系統將會出現FileNotFoundException,日誌相似下面這樣:java

java.io.FileNotFoundException:C:\Users\cheng\AppData\Local\Temp\tomcat.7543349588424487992.9000\work\Tomcat\localhost\ROOT\file\upload\tmp\file\upload\1558332190813.jpg (系統找不到指定的路徑。)

這是什麼緣由呢?能夠進入transferTo方法git

@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
    this.part.write(dest.getPath());
    if (dest.isAbsolute() && !dest.exists()) {
        // Servlet 3.0 Part.write is not guaranteed to support absolute file paths:
        // may translate the given path to a relative location within a temp dir
        // (e.g. on Jetty whereas Tomcat and Undertow detect absolute paths).
        // At least we offloaded the file from memory storage; it'll get deleted
        // from the temp dir eventually in any case. And for our user's purposes,
        // we can manually copy it to the requested location as a fallback.
        FileCopyUtils.copy(this.part.getInputStream(),         Files.newOutputStream(dest.toPath()));
    }
}

然後咱們再進入write方法github

@Override
public void write(String fileName) throws IOException {
    File file = new File(fileName);
    if (!file.isAbsolute()) {
        file = new File(location, fileName);
    }
    try {
        fileItem.write(file);
    } catch (Exception e) {
        throw new IOException(e);
    }
}

這時候咱們看到若是file.isAbsolute()成立,也就是咱們沒有使用絕對路徑,那麼file = new File(location,fileName);會在原來的基礎上加上location路徑.這就是緣由所在,能夠經過修改絕對路徑解決web

  1. 直接將file.upload.path修改成絕對路徑便可
  2. 在代碼中控制spring

    @PostMapping("/upload")
        public Map<String, String> upload(@RequestParam MultipartFile file) throws IOException {
            //建立本地文件
            String classpath = ResourceUtils.getURL("classpath:").getPath();
            File localFile = new File(classpath + path, file.getOriginalFilename());
            //把傳上來的文件寫到本地文件
            file.transferTo(localFile);
            //返回localFile文件路徑
            Map<String, String> path = new HashMap<>();
            path.put("path", localFile.getAbsolutePath());
            return path;
        }

    經過ResourceUtils.getURL("classpath:").getPath()得到項目路徑,而後加上設置的相對路徑。json

    網絡上還有一種修改location值的方式,能夠看 這篇博客可是我我的使用是一直不能夠。緩存

  3. 或者能夠不使用transferTo,代碼以下tomcat

    @PostMapping("/singleFileUpload")
    public String singleFileUpload(@RequestParam("file") MultipartFile file) throws IOException {
    byte[] bytes = file.getBytes();
        Path filePath = Paths.get(path + file.getOriginalFilename());
    Files.write(filePath, bytes);
        return file.getOriginalFilename();
    }

    Paths.get所使用的也是絕對路徑,若是您在Windows機器上使用了這種路徑(從/開始的路徑),那麼路徑將被解釋爲相對於當前驅動器,例如springboot

    /file/upload/1.txt

    而您的項目位於D盤。那麼這條路徑就會對應這條完整的路徑:網絡

    D:\file\upload\1.txt

爲了簡便,如下代碼均是使用絕對路徑。

文件上傳控制器編寫

多文件上傳

@PostMapping("/uploads")
public Map<String, String> uploads(@RequestParam MultipartFile[] files) throws IOException {
    StringBuilder sb = new StringBuilder();
    Map<String, String> paths = new HashMap<>();
    for (MultipartFile file : files) {
        //建立本地文件
        File localFile = new File(path, file.getOriginalFilename());
        //把傳上來的文件寫到本地文件
        file.transferTo(localFile);
        sb.append(localFile.getAbsolutePath()).append(",");
        paths.put(file.getOriginalFilename(), localFile.getAbsolutePath());
    }
    //返回localFile文件路徑
    return paths;
}

多文件上傳+表單提交

@PostMapping("/uploadsWithForm")
public Map<String, String> uploadsWithForm(@RequestParam String tmpString, @RequestParam MultipartFile[] files) throws IOException {
    StringBuilder sb = new StringBuilder();
    Map<String, String> paths = new HashMap<>();
    paths.put("tmpString", tmpString);
    for (MultipartFile file : files) {
        //建立本地文件
        File localFile = new File(path, file.getOriginalFilename());
        //把傳上來的文件寫到本地文件
        file.transferTo(localFile);
        sb.append(localFile.getAbsolutePath()).append(",");
        paths.put(file.getOriginalFilename(), localFile.getAbsolutePath());
    }
    //返回localFile文件路徑
    return paths;
}

多文件上傳+Json數據提交

@PostMapping(value = "/uploadsWithJson")
public Map<String, String> uploadsWithJson(@RequestPart("files") MultipartFile[] files, @RequestPart("jsonMap") Map<String, Object> jsonMap) throws IOException {

    StringBuilder sb = new StringBuilder();
    Map<String, String> paths = new HashMap<>();
    System.out.println(jsonMap);
    for (MultipartFile file : files) {
        //建立本地文件
        File localFile = new File(path, file.getOriginalFilename());
        //把傳上來的文件寫到本地文件
        file.transferTo(localFile);
        sb.append(localFile.getAbsolutePath()).append(",");
        paths.put(file.getOriginalFilename(), localFile.getAbsolutePath());
    }
    paths.put("jsonMap", JsonUtils.obj2json(jsonMap));
    //返回localFile文件路徑
    return paths;
}

呵呵,很差用對不對。項目拋出了個異常,HttpMediaTypeNotSupportedException

WARN  o.s.w.s.m.support.DefaultHandlerExceptionResolver - Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported]

因此咱們須要添加個轉換器類

@Component
public class MultipartJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {

    /**
     * Converter for support http request with header Content-Type: multipart/form-data
     */
    public MultipartJackson2HttpMessageConverter(ObjectMapper objectMapper) {
        super(objectMapper, MediaType.APPLICATION_OCTET_STREAM);
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return false;
    }

    @Override
    public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
        return false;
    }

    @Override
    protected boolean canWrite(MediaType mediaType) {
        return false;
    }
}

這樣就可以識別了

總結

感受把springboot文件上傳所能遇到的坑全踩了個變,心累。

若是須要項目代碼,能夠去個人github中下載;具體代碼能夠查看file.upload目錄

相關文章
相關標籤/搜索