Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send image data to server from Android

I am trying to write an Android application that will take a picture, put the data (byte[]) in an object along with some metadata, and post that to an AppEngine server where it will get persisted in a datastore as a blob. I don't really want to save the image as a file on Android (unless it's absolutely necessary). I searched around for solutions but nothing clear or specific enough came up. My questions are:

  1. how do I post the object to my servlet? Specifically how to properly serialize the object and get the serialized output to an HttpPost or something similar.
  2. once I persist the blob in the datastore, how will I be able to retrieve it as an image to display on a webpage?

Code examples would be very helpful. Also, if the approach I am taking is too complicated or problematic, please suggest other approaches (e.g. saving image as file, posting to server, then deleting).

like image 444
mstath Avatar asked Dec 02 '22 00:12

mstath


2 Answers

You just need to do a Http-FileUpload which in a special case of a POST. There is no need to uuencode the file. No need to use a special lib/jar No need to save the object to disk (regardless that the following example is doing so)

You find a very good explanation of Http-Command and as your special focus "file upload" under

Using java.net.URLConnection to fire and handle HTTP requests

The file upload sample from there follows (watch "send binary file") and it is possible to add some companion data either


String param = "value";
File textFile = new File("/path/to/file.txt");
File binaryFile = new File("/path/to/file.bin");
String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value.
String CRLF = "\r\n"; // Line separator required by multipart/form-data.

URLConnection connection = new URL(url).openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
PrintWriter writer = null;
try {
    OutputStream output = connection.getOutputStream();
    writer = new PrintWriter(new OutputStreamWriter(output, charset), true); // true = autoFlush, important!

    // Send normal param.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"param\"").append(CRLF);
    writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
    writer.append(CRLF);
    writer.append(param).append(CRLF).flush();

    // Send text file.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"textFile\"; filename=\"" + textFile.getName() + "\"").append(CRLF);
    writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
    writer.append(CRLF).flush();
    BufferedReader reader = null;
    try {
        reader = new BufferedReader(new InputStreamReader(new FileInputStream(textFile), charset));
        for (String line; (line = reader.readLine()) != null;) {
            writer.append(line).append(CRLF);
        }
    } finally {
        if (reader != null) try { reader.close(); } catch (IOException logOrIgnore) {}
    }
    writer.flush();

    // Send binary file.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"binaryFile\"; filename=\"" + binaryFile.getName() + "\"").append(CRLF);
    writer.append("Content-Type: " +     URLConnection.guessContentTypeFromName(binaryFile.getName()).append(CRLF);
    writer.append("Content-Transfer-Encoding: binary").append(CRLF);
    writer.append(CRLF).flush();
    InputStream input = null;
    try {
        input = new FileInputStream(binaryFile);
        byte[] buffer = new byte[1024];
        for (int length = 0; (length = input.read(buffer)) > 0;) {
            output.write(buffer, 0, length);
        }
        output.flush(); // Important! Output cannot be closed. Close of writer will close output as well.
    } finally {
        if (input != null) try { input.close(); } catch (IOException logOrIgnore) {}
    }
    writer.append(CRLF).flush(); // CRLF is important! It indicates end of binary boundary.

    // End of multipart/form-data.
    writer.append("--" + boundary + "--").append(CRLF);
} finally {
    if (writer != null) writer.close();
}

Regarding the second part of your question. When successful uploading the file (I use apache common files), it is not a big deal to deliver a blob as an image.

This is how to accept a file in a servlet

public void doPost(HttpServletRequest pRequest, HttpServletResponse pResponse)
        throws ServletException, IOException {
    ServletFileUpload upload = new ServletFileUpload();

    try {
        FileItemIterator iter = upload.getItemIterator (pRequest);

        while (iter.hasNext()) {
            FileItemStream item = iter.next();

            String fieldName = item.getFieldName();

                InputStream stream = item.openStream();
            ....
                stream.close();
            }
    ...

And this code delivers an image

public void doGet (HttpServletRequest pRequest, HttpServletResponse pResponse)  throws IOException {

    try {
        Blob img = (Blob) entity.getProperty(propImg);

        pResponse.addHeader ("Content-Disposition", "attachment; filename=abc.png");
        pResponse.addHeader ("Cache-Control", "max-age=120");

        String enc = "image/png";
        pResponse.setContentType (enc);
        pResponse.setContentLength (img.getBytes().length);
        OutputStream out = pResponse.getOutputStream ();
        out.write (img.getBytes());
        out.close();

I hope this code fragments help to answer your questions

like image 195
stefan bachert Avatar answered Dec 16 '22 02:12

stefan bachert


1.You can encode the byte[] to base64 using:

http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Base64.html

2.Send that data with a HTTP POST request to your AppEngine servlet.

3.Configure AppEngine to accept a servlet.

4.Then you have the choice to save it to the Datastore or the Blobstore. -I prefer the blobstore for these kinds of things.

5.Decode the base64 string server side.

6.From there on you'l need to cut up your string into smaller pieces and write each piece to the blobstore.

Here's some code for writing it to the blobstore.

byte[] finalImageArray = null;

    try {
        finalImageArray = Base64.decode(finalImageData.getBytes()); //finalImageData is the string retrieved from the HTTP POST
    } catch (Base64DecoderException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }


    try{
        FileService fileService = FileServiceFactory.getFileService();

        AppEngineFile file = fileService.createNewBlobFile("image/png");

        FileWriteChannel writeChannel = fileService.openWriteChannel(file, true);

        int steps = (int) Math.floor(finalImageArray.length/1000);
        int current = 0;
        for(int i = 0; i < 1000; i++){
            writeChannel.write(ByteBuffer.wrap(Arrays.copyOfRange(finalImageArray, current, steps+current)));
            current = current + steps;
        }
        writeChannel.write(ByteBuffer.wrap(Arrays.copyOfRange(finalImageArray, current, finalImageArray.length)));  //The reason it's cut up like this is because you can't write the complete string in one go.

        writeChannel.closeFinally();

        blobKey = fileService.getBlobKey(file);

        if(blobKey == null)
            blobKey = retryBloBKey(file); //My own method, AppEngine tends to not return the blobKey once a while.

    }
    catch(Exception e){
        logger.log(Level.SEVERE,e.getMessage());
    }


            return blobKey.getKeyString();
  1. Write a servlet where you retrieve the image data with the provided key.

  2. Enjoy your beautiful code :)

//The reason i save it in binary is because that gives me options to play with the image api, you can also choose to save it in the base64 format.

like image 34
Rohan Avatar answered Dec 16 '22 03:12

Rohan