【问题标题】:MultipartFile issue, unable to convert to FileMultipartFile 问题,无法转换为文件
【发布时间】:2020-03-02 06:02:47
【问题描述】:

我正在尝试上传 1 GB 以上的文件,我正在使用 Spring Boot。

我已尝试使用以下代码,但出现内存不足错误。

public void uploadFile(MultipartFile file) throws IOException {
        try {       
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

            SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
            requestFactory.setBufferRequestBody(false);
            restTemplate.setRequestFactory(requestFactory);

            String uploadFile= restTemplate.exchange(url, HttpMethod.POST,
                new HttpEntity<>(new FileSystemResource(convert(file)), headers), String.class).getBody();

        } catch (Exception e) {
            throw new RuntimeException("Exception Occured", e);
        }
    }


    private static File convert(MultipartFile file) throws IOException {
        File convFile = new File(file.getOriginalFilename());
        convFile.createNewFile();
        FileOutputStream fos = new FileOutputStream(convFile);
        fos.write(file.getBytes());
        fos.close();
        return convFile;    
    }

我面临的主要问题是,我无法将 MultipartFile 转换为 java.io.File。

我什至尝试将FileSystemResource 替换为ByteArrayResource,但仍然出现OOM 错误。

我什至也尝试过使用以下代码:

private static File convert(MultipartFile file) throws IOException {
        CommonsMultipartFile commonsMultipartFile = (CommonsMultipartFile) multipartFile;
        FileItem fileItem = commonsMultipartFile.getFileItem();
        DiskFileItem diskFileItem = (DiskFileItem) fileItem;
        String absPath = diskFileItem.getStoreLocation().getAbsolutePath();
        File file = new File(absPath);  
    }

但是对于上面的 sn-p,我得到了以下异常:

org.springframework.web.multipart.commons.CommonsMultipartFile 不能 转换为 org.springframework.web.multipart.MultipartFile

  1. 谁能告诉我如何将 MultipartFile 转换为 java.io.File?

  2. 还有比FileSystemResource更好的方法吗?因为我每次上传前都必须在服务器中创建新文件。如果文件超过 1GB,则必须在服务器端创建另一个 1GB 的新文件,并且必须再次手动删除该文件,我个人不喜欢这种方法。

【问题讨论】:

    标签: java spring rest spring-boot spring-mvc


    【解决方案1】:

    getBytes() 尝试将整个字节数组加载到内存中,这导致您的OOM 您需要做的是流式传输文件并将其写出。

    尝试以下方法:

    private static Path convert(MultipartFile file) throws IOException {
      Path newFile = Paths.get(file.getOriginalFilename());
      try(InputStream is = file.getInputStream();
         OutputStream os = Files.newOutputStream(newFile))) {
         byte[] buffer = new byte[4096];
         int read = 0;
         while((read = is.read(buffer)) > 0) {
           os.write(buffer,0,read);
         }
      }
      return newFile;  
    }
    

    我更改了您的方法以返回 Path,而不是 File,它是 java.nio 包的一部分。该软件包比java.io 更受欢迎,因为它已经过更多优化。

    如果您确实需要 File 对象,您可以调用 newFile.toFile()

    由于它返回一个Path 对象,因此您可以使用java.nio.file.Files 类在文件被写出后将其重新定位到您的首选目录

    private static void relocateFile(Path original, Path newLoc) throws IOException {
      if(Files.isDirectory(newLoc)) {
        newLoc = newLoc.resolve(original.getFileName());
      }
      Files.move(original, newLoc);
    }
    

    【讨论】:

    • 请注意,您也可以使用Files.copy(is, newFile),或者,如果使用Java 9+,则使用is.transferTo(os)。但是,您确实会失去对缓冲区大小的控制。
    • 这些也是很好的建议。我不确定Files.copy 在后台是如何工作的(没有查看源代码),但我倾向于控制缓冲区大小,尤其是在资源有限的机器上
    • ByteArrayOutputStream 仍然将字节存储在内存中,因为您没有在读取时将其写入文件,因此OOM
    • 不,InputStream 只是一个抽象。 BufferedInputStream 在底层实现了InputStream,然后添加了一些您可以利用的更多功能。两者都读取一段信息并将其存储到内存中。看看这个答案以获得更好的解释stackoverflow.com/questions/2964044/…
    猜你喜欢
    • 1970-01-01
    • 2020-04-11
    • 1970-01-01
    • 2013-05-31
    • 1970-01-01
    • 2023-03-05
    • 2020-05-07
    • 2018-12-10
    • 2013-08-10
    相关资源
    最近更新 更多