1

In my current spring project, I have a form with some input[type=file] fields which need be handled by this PropertyEditorSupport class:

public class ImagemEditor extends PropertyEditorSupport {
  private String file_path = System.getProperty("user.home")+File.separator+".store"+File.separator+"Pictures";

  @Override
  public void setAsText(String text) {
    ...
  }
  ...
}

the image is sent to server as a Base64 String and it's added to the other params by this javascript code:

  $('input[type=file]').on("change", function(){
    var id = $(this).attr("id");
    var name = $(this).attr("name");
    if(typeof id !== "undefined") {
      if(this.files.length > 0) {
        reader = new FileReader();
        reader.onloadend = function () {
          str += "&" + name + "=" + this.result;
        }
        reader.readAsDataURL(this.files[0]);
      }
    }
  });

In the PropertyEditorSupport class, I read the String with the Base64 encoded image and convert to byte[], just to store this bytes into a file:

  byte[] buffer = Base64.decodeBase64(text.split(",")[1]);

  File arquivo;
  try {
    arquivo = new File(file_path+File.separator+file_name()+".jpeg");
  } catch (Exception e) {
    e.printStackTrace();
    arquivo = null;
  }

  File dir = new File(file_path);
  if(!dir.exists())
    dir.mkdirs();
  if(!arquivo.exists())
    try {
      arquivo.createNewFile();
    } catch (Exception e) {
      e.printStackTrace();
    }

  FileOutputStream fileOut;
  try {
    fileOut = new FileOutputStream(arquivo);
  } catch (Exception e) {
    e.printStackTrace();
    fileOut = null;
  }

  try {
    fileOut.write(buffer);
  } catch (Exception e) {
    e.printStackTrace();
  }

  try {
    fileOut.close();
  } catch (Exception e) {
    e.printStackTrace();
  }

but when I try open the resulting image, it isn't the same image I upload (I use the command line tool vbindiff to verify that, and the header of the image is always the same). It's not even possible open the resulting image (I am using Gwenview on Linux/Kubuntu).

Someone can see what's wrong here?

Kleber Mota
  • 8,521
  • 31
  • 94
  • 188
  • The buffer here new ByteArrayInputStream(buffer) is the same as the buffer here byte[] buffer ? – reos Mar 02 '17 at 20:27
  • yes, it's the same variable. – Kleber Mota Mar 02 '17 at 20:48
  • hmmm I suggest you to use a codification, something basic as base64. You code the image in the client and then in the server you decode. – reos Mar 02 '17 at 20:53
  • In the server this is done with `StandartCharset` added to `getBytes(...)`? Or it's other thing? And in the client? Do you know how encode the parameter? – Kleber Mota Mar 02 '17 at 20:59
  • 1
    You can search for javascript base64 encoding and java base64 decoding. – reos Mar 02 '17 at 21:00
  • Refer this link http://stackoverflow.com/questions/17710147/image-convert-to-base64 – Vijendra Kumar Kulhade Mar 02 '17 at 23:28
  • @VijendraKulhade @reos the javascript part is working fine. The decoding in the Java class is where lives the problem. Right now, I change the code to this: `String valueEncoded = text.split(",")[1]; byte[] buffer = Base64.getUrlDecoder().decode(valueEncoded);`, and now the code do not go beyond that line somehow (no error is thrown). – Kleber Mota Mar 02 '17 at 23:48
  • I would have done it like `byte[] imageByteCode = org.apache.commons.codec.binary.Base64.decodeBase64(valueEncoded);` – Vijendra Kumar Kulhade Mar 02 '17 at 23:52
  • @VijendraKulhade Ok, this change helped, at least now I am getting the exception: `javax.imageio.IIOException: Invalid JPEG file structure: two SOF markers` for `ImageIO.read`, but the execution continues, and the `ImageIO.write` throws the exception: `java.lang.IllegalArgumentException: image == null!`. (the image I am trying to send open without problems). – Kleber Mota Mar 03 '17 at 11:53
  • 4
    While adding the bounty might help you, I think you could already have had an answer, if you had only bothered to present a problem that is *reproducible*. Random code and error messages that tells you that the input is corrupted doesn't really help much unless you actually provide the *input* that caused those errors... – Harald K Mar 05 '17 at 10:16
  • @haraldK any jpeg image can be used to repreoduce this situation, I guess. In my case, I am just doing that: taking a random image (which open without problem with other image viewers) and try submit through the form of my application. – Kleber Mota Mar 05 '17 at 17:01
  • Remember that strings in JS are encoded as UTF16 and not UTF8 while the decodeBase64() requires a byte array. There is really no need to go through the hoops of converting to and from string. You can convert the File blob directly to ArrayBuffer (ie., byte array) and send that as raw bytes. Then on Java side consume the raw byte array without going through base-64 decoding... –  Mar 10 '17 at 10:42
  • 1
    To throw in another option, just use multipart/form-data for file uploading. – beat Mar 10 '17 at 13:04
  • `readAsDataURL` is going to give you a data URL, which is not entirely a Base64 encoded string- it is preceded by the MIME type among other things. Have you considered `readAsArrayBuffer` instead? – A.Konzel Mar 10 '17 at 17:59
  • @SaxxonPike I notice that, and because this I am using `text.split(",")[1]`, to read only the Base64 part of the string and not the MIME type part. – Kleber Mota Mar 10 '17 at 23:11
  • @K3N By what I see in the official documentation, decodeBase64 accept both byte array and String. By what I know, to convert the File blob to ArrayBuffer I should use `readAsArrayBuffer` right? How I consume the raw byte in my Java class (if the method setAsText from PropertyEditorSupport class requires a String)? – Kleber Mota Mar 10 '17 at 23:14

1 Answers1

3

I tried to put up a very short example using just the jre.

You just need to put the html in index.html in the working directory, run the server and upload an example image.

This is just example code, so your app will be running on a servlet container of some kind, you will have to adapt code to the actual request and response object you have.

Index Page

<html>
<head>
<title>Test file</title>
<script type="text/javascript">
    function sendFile() {
        var file = document.querySelector('input[type=file]').files[0];
        var reader = new FileReader();

        reader.addEventListener("load", function() {
            var http = new XMLHttpRequest();
            var url = "save_file";

            http.open("POST", url, true);
            http.setRequestHeader("Content-type",
                    "application/x-www-form-urlencoded");
            http.onreadystatechange = function() {//Call a function when the state changes.
                if (http.readyState == 4 && http.status == 200) {
                    console.info(http.responseText);
                }
            }
            var header = "base64,";
            var pos=reader.result.indexOf(header);
            var data = reader.result.substring(pos+header.length);
            http.send(data);
        }, false);

        if (file) {
            reader.readAsDataURL(file);
        }
    }
</script>
</head>
<body>
    <input type="file" onchange="sendFile()">
    <br>
</body>
</html>

The HTTP Server

package so;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.Base64;
import java.util.Scanner;


import com.sun.net.httpserver.*;

public class LoadImage {

    public static void main(String[] args) throws IOException {
         HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
         server.createContext("/save_file",FileSaveHandler());
         server.createContext("/", indexHandler());
         server.start();
         System.out.println("Server started");
         Scanner scanner = new Scanner(System.in);
         scanner.nextLine();
         System.out.println("Server stopped");
         server.stop(0);
    }

    private static HttpHandler indexHandler() {
        return new HttpHandler() {
            @Override
            public void handle(HttpExchange exchange) throws IOException {
                File f = new File("index.html");
                try(OutputStream responseBody = exchange.getResponseBody();InputStream in =  new FileInputStream(f);){
                    byte[] buffer = new byte[(int)f.length()];
                    in.read(buffer);
                    exchange.sendResponseHeaders(200, buffer.length);
                    responseBody.write(buffer);
                }
            }
        };
    }

    private static HttpHandler FileSaveHandler() {
        return new HttpHandler() {

            @Override
            public void handle(HttpExchange exchange) throws IOException {

                try(InputStream in = exchange.getRequestBody();
                    OutputStream out = new FileOutputStream("out.jpg")){

                    byte [] buffer = new byte[3*1024];
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    int l = 0;
                    while((l=in.read(buffer))>=0){
                        bos.write(buffer, 0, l);
                    }
                    byte[] data = Base64.getDecoder().decode(bos.toByteArray());
                    out.write(data);
                }

            }
        };
    }
}
minus
  • 2,646
  • 15
  • 18