5

I am using Spring Boot as backend server and I have a JavaScript frontend. For sending data between front- and backend I'm using the Axios library, which usually works pretty fine.

The Problem:
The image looks like this in the (Chrome) browser console: Console output It's a very very long alphanumeric string and that's what I send to the server with the following code:

static uploadFiles(files) {
    const data = new FormData();

    Object.keys(files).forEach(key => {
        data.append("files", new Blob([files[key]], { type: 'image/jpeg' }));
    });

    const url = API_URL + "uploadFiles";
    return axios.post(url, data, RestServices.getAuth({
        "Content-Type": "multipart/form-data;boundary=gc0p4Jq0M2Yt08jU534c0p"
    }));
}

I have no idea what the boundary thing does but it worked to receive a file in the backend tho...

On backend (spring) side I successfully receive an array of MultipartFiles:

@RequestMapping(value = "/uploadFiles", method = RequestMethod.POST)
@ResponseBody
public boolean uploadFiles(HttpServletRequest request, @RequestParam("files") MultipartFile[] files) throws IOException {
    String filePath = Thread.currentThread().getContextClassLoader().getResource("assets/images/").getFile();
    InputStream inputStream;
    OutputStream outputStream;
    for(MultipartFile file : files) {
        File newFile = new File(filePath + file.getOriginalFilename() + ".jpg");
        inputStream = file.getInputStream();

        if (!newFile.exists() && newFile.createNewFile()) {
            outputStream = new FileOutputStream(newFile);
            int read;
            byte[] bytes = new byte[1024];

            while ((read = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, read);
            }
        }
        System.out.println(newFile.getAbsolutePath());
    }
    return true;
}

I've also tried it file.transferTo(newFile); instead of in- and outputstreams - which didn't work either.
After that I get the following output, which means that the image was saved successfully: /path/to/blob.jpg
If I check the path where the file was uploaded, there is a file named blob.jpg, but if I open it, the windows photo viewer has the following problem: Error in windows photo viewer I've opened the image before and after upload with notepad++:
Before upload: Image before upload (Byte Array) I think this is a byte array, but If I open the image after upload I get exactly the output of the browser. This means it didn't get converted to a byte array (correct me if I'm wrong) and I believe that's why it's a corrupt image...

My questions are:

  • What's the problem?
  • How can I fix it?

I really tried everything which crossed my mind but I ran out of ideas.

Thanks for your help! :-)


I've read following *related* questions (but they **don't** have an answer):
[Question1][5], [Question2][6], and **many** more...

3
  • Basically it's the difference between sending bytes or Base64 encoded bytes. Commented Nov 16, 2017 at 13:16
  • Meanwhile I've tried this: outputStream.write(Base64.getEncoder().encode(bytes), 0, read);, but still the same error. Maybe there's a problem with encoding the image prefix (data:image/jpeg)? @Kayaman But I don't even know which type (encoded or not) I'm sending... Commented Nov 16, 2017 at 13:22
  • 2
    You can't "decrypt" a hash. It's a one way function. Commented Nov 16, 2017 at 14:13

1 Answer 1

4

I've finally found an answer on my own!

I think the problem was that I used the e.target.result (which is used to show the image on the frontend) but insted I had to use the JS File object. The standard HTML 5 file input fields return those File objects (as I've read here).

The only thing I had to do now is to make a FormData object, append the File Object, set the FormData as Body and set the Content-Type header and that's it!

const data = new FormData();
data.append("files", fileObject);
return axios.post(url, data, {
    "Content-Type": "multipart/form-data"
});

Those JS File Objects are recognized from Java as Multipart files:

@RequestMapping(value = "/uploadFiles", method = RequestMethod.POST)
@ResponseBody
public boolean uploadFiles(HttpServletRequest request, @RequestParam("files") MultipartFile[] files) {

    boolean transferSuccessful = true;

    for (MultipartFile file : files) {
        String extension = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf('.'));

        String newFileName = genRandomName() + extension; //set unique name when saving on server
        File newFile;

        File imageFolder = new File(imageBasePath);
        //check if parent folders exist else create it
        if(imageFolder .exists() || imageFolder .mkdirs()) {
            while ((newFile = new File(imageFolder .getAbsolutePath() + "\\" + newFileName)).exists()) {
                newFileName = genRandomName(); //generate new name if file already exists
            }
            try {
                file.transferTo(newFile);
            } catch (IOException e) {
                e.printStackTrace();
                transferSuccessful = false;
            }
        } else {
            LOG.error("Could not create folder at " + imageFolder.getAbsolutePath());
            transferSuccessful = false;
        }
    }
    return transferSuccessful;
}

I hope this is helpful :)

Sign up to request clarification or add additional context in comments.

3 Comments

Great answer. saved my day
How did you convert the e.target.result File to a JS File object?
You automatically get a File object if you use an input field with type 'file' see here: developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.