1

The line file1.createNewFile(); creates a new file, but the file.transferTo(file1); does not transfer the contents of the MultipartFile to file1. Is there a way to make the transferTo method work?

@PostMapping("/form3")
public String handleFormUpload3(@RequestParam("file") MultipartFile file) throws IOException {
    String fileName = file.getOriginalFilename();
    File file1 = new File(fileName);
    if(!file1.exists())
        file1.createNewFile();
    file.transferTo(file1);
    System.out.println(file1.exists());
    return "redirect:uploadSuccess";
}

When I use FileOutputStream to save the MultipartFile to a regular file by converting into to a byte array, the transfer works fine. But transferTo() is much simpler and less verbose. How do I make it work?

  • Where do you expect to see your file? I can see you have created new File without any path, and `getOrifinalFileName` is not going to give you a file path, make sure you knew where the current directory is. – Popeye May 19 '23 at 07:30
  • @Popeye. The file does get created as mentioned in the outermost folder in the project. The issue I face is that the `transferTo()` does not work in my code. I saw your other comment that it worked for you. Please let me know if the exact same code worked? – Gautham Muralidharan May 19 '23 at 10:09
  • The exact code you have written. Please be more specific on how it does not work, Does it throw exception? Is it just no files are created? – Popeye May 19 '23 at 22:03
  • Give an absolute path, if you use `File` the renamed file got created in `ROOT` directory of `TOMCAT`, I can explain the reason, but then I will have to offer a complete answer. – Popeye May 19 '23 at 22:50
  • The code gets executed successfully without throwing any exceptions. File gets created thanks to `createNewFile()` But the MultipartFile did not get transferred to the file. – Gautham Muralidharan May 20 '23 at 05:31
  • As I said in my last comment , if you do not provide absolute path , your code will not execute properly. In production environment it will start writing file in your home directory, worse case, somewhere where it won't even have permission to write. Also creating file before moving is unnecessary. – Popeye May 20 '23 at 09:59

2 Answers2

2

I did a little digging on the source code itself to find out why this is happening.

In the class org.springframework.web.multipart.support.StandardMultipartHttpServletRequest two functions exists

        @Override
        public void transferTo(File dest) throws IOException, IllegalStateException {
            this.part.write(dest.getPath());
            if (dest.isAbsolute() && !dest.exists()) {
                FileCopyUtils.copy(this.part.getInputStream(), Files.newOutputStream(dest.toPath()));
            }
        }

        @Override
        public void transferTo(Path dest) throws IOException, IllegalStateException {
            FileCopyUtils.copy(this.part.getInputStream(), Files.newOutputStream(dest));
        }
    }

Definition of part.write() method is written in class org.apache.catalina.core.ApplicationPart

    @Override
    public void write(String fileName) throws IOException {
        File file = new File(fileName);
        // testing whether the path is absolute or not, OP's code is relative path
        if (!file.isAbsolute()) {
          // HIGHLIGHTED PART
            file = new File(location, fileName);
        }
        try {
            fileItem.write(file);
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

In the write method absolute path is being created by using location variable. Variable location hold the value of ROOT directory of TOMCAT as example

/tmp/tomcat.8080.12533880900101227658/work/Tomcat/localhost/ROOT/

So, when you do not give an absolute path to write, function simply decide to move the file to ROOT directory. In other word, file is being moved, working properly, OP just does not know where to look for the moved file.

What can be done to resolve OP''s issue.

class ControllerClass {
  // Create an absolute base path where your uploaded file will be moved. 
  // All file will be moved here 
  private final String uploadFilePath = "/a/suitable/path/";
  @PostMapping("/form3")
  public String handleFormUpload3(@RequestParam("file") MultipartFile file) throws IOException {
      String fileName = file.getOriginalFilename();
      File file1 = new File(uploadFilePath, fileName);
      //if(!file1.exists())
      //    file1.createNewFile();
      file.transferTo(file1);
      System.out.println(file1.exists());
      return "redirect:uploadSuccess";
  }
}

PS This is poor judgement of coding by Spring Boot development team. They have overwridden function that has different behvior. This is bad principle for writing library. It is unwise to force programmer to be hyper aware of how functions with same name behave. It just create distrust towards the library. Both of the functions should have behave exactly the same. My apporach would be simpley call the other function with proper value instead of writing a whole new function with different nature.

Popeye
  • 365
  • 3
  • 13
0

I have reproduced your case and can confirm that transferTo() with File object doesn't work. I don't know why. But transferTo() with Path works fine.

This code works fine:

String fileName = file.getOriginalFilename();
File file1 = new File(fileName);
file.transferTo(file1.toPath());

EDIT: I have checked the source code (Java 20) and there is an obvious difference between (1) transferTo(File file) and (2) transferTo(Path path): in (1) the file will only be copied if it is an absolute path. It means starts with '/' on Unix or drive letter or network drive on Windows. Method (2) doesn't check it and just copy the file. In our case to the working directory as the file name is not prefixed with anything.

Mar-Z
  • 2,660
  • 2
  • 4
  • 16
  • Thank you very much for the suggestion. `file.transferTo(file1.toPath());` works. `transferto(File file)` and `transferto(Path path)` both exists. Interestingly, `transferto(File file)` does not work for some reason. – Gautham Muralidharan May 19 '23 at 06:41
  • 1
    Yes. File is from the "ancient" Java package java.io Path is from the new one java.nio.file Anyway thanks for feedback. – Mar-Z May 19 '23 at 06:47
  • For me, both works. I also checked the source code, it is very odd. the difference in the two functions are kind of funny. But Both functions works. – Popeye May 19 '23 at 07:31
  • @Popeye probably different Java versions? I'm on Java 20 – Mar-Z May 19 '23 at 07:35
  • @Mar-Z please check other conditions. I do not see anything suspicious in Springboot source code. I am using java-19. But that's not a good reason to assume old functions broke. – Popeye May 19 '23 at 08:22
  • @Mar-Z I tried using both Java 20 and 17, `transferto(File file)` does not work in both. – Gautham Muralidharan May 19 '23 at 09:01
  • Yes. Please check the updated answer. @Popeye we are not assuming anything - we are testing :) – Mar-Z May 19 '23 at 09:06