11

i'm having issues with uploading images in android.

i'm using apache httpmime 4.1 lib the code is like this:

MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);

reqEntity.addPart("image", new FileBody(new File(AndorraApplication.getPhotosPath() + "/" + entity.getFileName()), "image/jpeg"));
resp = NetworkUtils.sendHttpRequestMultipart(EXPORT_PHOTOS_URI, reqEntity);

NetworkUtils class:

public class NetworkUtils {
    public static final int REGISTRATION_TIMEOUT = 3 * 1000; 
    public static final int WAIT_TIMEOUT = 5 * 1000;

    public static HttpResponse sendHttpRequestMultipart(String uri, MultipartEntity entity) {
        HttpClient mHttpClient = new DefaultHttpClient();
        final HttpParams params = mHttpClient.getParams();
        HttpConnectionParams.setConnectionTimeout(params, REGISTRATION_TIMEOUT);
        HttpConnectionParams.setSoTimeout(params, WAIT_TIMEOUT);
        ConnManagerParams.setTimeout(params, WAIT_TIMEOUT);

        HttpPost post = new HttpPost(uri);
        post.addHeader(entity.getContentType());
        post.setEntity(entity);
        HttpResponse resp = mHttpClient.execute(post);
    }
}

sometimes everything works fine, but sometimes (especially on a slow connection) the image is uploaded very corrupted. the example is here: http://pixelbirthcloud.com/574_orig.jpg

it doesn't throw any exceptions. the lenght of the uploaded file is same as the original one.. tried to change mime type to application/octet-stream or remove it at all. tries playing with timeouts. still same result. end users upload corrupted images almost all the time (although i managed to get bronem images just 2 times only).. the image size was 2.5 megs at first, but then i reduced it to 500-700 kb. didn't fix the problem though.

haven't tried to change the apache's library.. maybe it is the problem.. but as far as i read the net, nobody was experiencing this with httpmime library.

what can it be? i'm completely lost now :(

the other issue is that timeouts sometimes not working.

like when it comes to this line: HttpResponse resp = mHttpClient.execute(post); and i disable 3g connection it just waits for like 17-20 minutes instead of 3 or 5 seconds.. and only then throws exception. tried different methods. like this:

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
        HttpProtocolParams.setUseExpectContinue(params, false);  
        HttpConnectionParams.setConnectionTimeout(params, 10000);
        HttpConnectionParams.setSoTimeout(params, 10000);
        ConnManagerParams.setMaxTotalConnections(params, 5);
        ConnManagerParams.setTimeout(params, 30000);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http",PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https",PlainSocketFactory.getSocketFactory(), 80));
        ThreadSafeClientConnManager manager = new ThreadSafeClientConnManager(params, registry);
        HttpClient httpclient = new DefaultHttpClient(manager, params);

but still doesn't work :)

Alex
  • 536
  • 1
  • 5
  • 13

3 Answers3

8

See My Code of Image Uploader and it worked great for me This class Uploads a file to the server plus in the end read the XML reply also. Filter the code as per your requirement.. It worked pretty smooth for me


package com.classifieds;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;


import android.util.Log;

public class Uploader 
{

    private String Tag = "UPLOADER";
    private String urlString ;//= "YOUR_ONLINE_PHP";
    HttpURLConnection conn;
    String exsistingFileName ;

    private void uploadImageData(String existingFileName , String urlString)
    {
        String lineEnd = "\r\n";
        String twoHyphens = "--";
        String boundary = "*****";
        try {
            // ------------------ CLIENT REQUEST

            Log.e(Tag, "Inside second Method");

            FileInputStream fileInputStream = new FileInputStream(new File(
                    exsistingFileName));

            // open a URL connection to the Servlet

            URL url = new URL(urlString);

            // Open a HTTP connection to the URL

            conn = (HttpURLConnection) url.openConnection();

            // Allow Inputs
            conn.setDoInput(true);

            // Allow Outputs
            conn.setDoOutput(true);

            // Don't use a cached copy.
            conn.setUseCaches(false);

            // Use a post method.
            conn.setRequestMethod("POST");

            conn.setRequestProperty("Connection", "Keep-Alive");

            conn.setRequestProperty("Content-Type",
                    "multipart/form-data;boundary=" + boundary);

            DataOutputStream dos = new DataOutputStream(conn.getOutputStream());

            dos.writeBytes(twoHyphens + boundary + lineEnd);
            dos
                    .writeBytes("Content-Disposition: post-data; name=uploadedfile;filename="
                            + exsistingFileName + "" + lineEnd);
            dos.writeBytes(lineEnd);

            Log.v(Tag, "Headers are written");

            // create a buffer of maximum size

            int bytesAvailable = fileInputStream.available();
            int maxBufferSize = 1000;
            // int bufferSize = Math.min(bytesAvailable, maxBufferSize);
            byte[] buffer = new byte[bytesAvailable];

            // read file and write it into form...

            int bytesRead = fileInputStream.read(buffer, 0, bytesAvailable);

            while (bytesRead > 0) {
                dos.write(buffer, 0, bytesAvailable);
                bytesAvailable = fileInputStream.available();
                bytesAvailable = Math.min(bytesAvailable, maxBufferSize);
                bytesRead = fileInputStream.read(buffer, 0, bytesAvailable);
            }

            // send multipart form data necesssary after file data...

            dos.writeBytes(lineEnd);
            dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

            // close streams
            Log.v(Tag, "File is written");
            fileInputStream.close();
            dos.flush();
            dos.close();

        } catch (MalformedURLException ex) {
            Log.e(Tag, "error: " + ex.getMessage(), ex);
        }

        catch (IOException ioe) {
            Log.e(Tag, "error: " + ioe.getMessage(), ioe);
        }


        SAXParserFactory spf = SAXParserFactory.newInstance();
        SAXParser sp = null;
        try {
            sp = spf.newSAXParser();
        } catch (ParserConfigurationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SAXException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // Get the XMLReader of the SAXParser we created.
        XMLReader xr = null;
        try {
            xr = sp.getXMLReader();
        } catch (SAXException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // Create a new ContentHandler and apply it to the XML-Reader
        MyExampleHandler1 myExampleHandler = new MyExampleHandler1();
        xr.setContentHandler(myExampleHandler);

        // Parse the xml-data from our URL. 
        try {
            xr.parse(new InputSource(conn.getInputStream()));
        //xr.parse(new InputSource(new java.io.FileInputStream(new java.io.File("login.xml")))); 
        } catch (MalformedURLException e) {
            Log.d("Net Disconnected", "NetDisconeeted");
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            Log.d("Net Disconnected", "NetDisconeeted");
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SAXException e) {
            Log.d("Net Disconnected", "NetDisconeeted");
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // Parsing has finished.

    }

    public Uploader(String existingFileName, boolean isImageUploading , String urlString ) {

        this.exsistingFileName = existingFileName;
        this.urlString = urlString;

    }

    class MyExampleHandler1 extends DefaultHandler
    {
    // ===========================================================
    // Methods
    // ===========================================================

    @Override
    public void startDocument() throws SAXException {

    }

    @Override
    public void endDocument() throws SAXException {
        // Nothing to do
    }

    @Override
    public void startElement(String namespaceURI, String localName,
              String qName, Attributes atts) throws SAXException {


    }

    /** Gets be called on closing tags like:
     * </tag> */
    @Override
    public void endElement(String namespaceURI, String localName, String qName)
              throws SAXException {


    }

    /** Gets be called on the following structure:
     * <tag>characters</tag> */
    @Override
    public void characters(char ch[], int start, int length) {

     }
    }

}
Rohit Sharma
  • 13,787
  • 8
  • 57
  • 72
  • The Online Script you will hit should have code to read file and save it on database – Rohit Sharma Feb 04 '11 at 10:39
  • may end up making something like this i guess... though it didn't answer the question, but thank you.. saw such solution on the net before, but decided to go a more convenient way - using httpcomponents.. but failed for some reason :( – Alex Feb 07 '11 at 07:02
  • its a complete class we just need to pass tha file url and the url of script. what would be more convenient then this – Rohit Sharma Feb 07 '11 at 07:43
  • httpmime library. =) much easier to use actually. made my own class based on this. takes files or text params... hope it will be workin the way i expect =) thank you – Alex Feb 07 '11 at 09:50
  • had no success with this :( the upload works w/o any errors, but the images are still corrupted. :( have you experienced any problems with this? seems like some blocks of bytes are taken from somewere else, while most of the file is same :( – Alex Feb 07 '11 at 11:53
  • i didnt face any errors can you post the image of uploaded image( Corrupted and Original ) – Rohit Sharma Feb 07 '11 at 11:56
  • i wonder how it is possibly create a error, if you check the code it simply using stream readers and writers and writing each and every byte of file. You must check the server side code for some test. how is it saving image and in which format – Rohit Sharma Feb 07 '11 at 11:58
  • check this link also http://getablogger.blogspot.com/2008/01/android-how-to-post-file-to-php-server.html – Rohit Sharma Feb 07 '11 at 12:01
  • i wish i knew! the server side is as simple as it can be. move_uploaded_file(blabla). if you look at here: http://stackoverflow.com/questions/4920912/android-image-corruption-when-uploading-via-http - this is my another post describing what exactly is happening to an image.. it is crazy, but it is a fact :) it is the only reason i posted a question in here... – Alex Feb 07 '11 at 12:08
  • i didnt face such issue for sure. the two images here shown has a corruption for sure. but i uploaded larger images as well. it went smooth . the link above i mention has a php code example . upload a php file containing this code, and test over it using the same code. and see the result – Rohit Sharma Feb 07 '11 at 12:13
  • and after all log cat the bytes that are getting written over the request and see whether it is corrupted or not. if not then its a clear error from server side or any network issue. – Rohit Sharma Feb 07 '11 at 12:16
  • what android version have you run this class on? i am on froyo.. maybe it's the problem? saw some notices that some programs had corrupt image upload issues... but no workaround for that except a checksum control, which is not applicable here, cause 98% of images come through corrupted... so it would be just a waste of traffic i guess – Alex Feb 07 '11 at 15:22
  • hmmm. i checked it on 1.6 Android. i will test it on 2.2 too and will let you know the result soon. – Rohit Sharma Feb 08 '11 at 04:28
  • 1
    noticed 1 thing: the slower connection is, the more corrupted images are – Alex Feb 08 '11 at 06:30
  • how can i upload multiple images with this method ? – Bunny Aug 07 '15 at 09:39
4

I had the same corruption problem on 80% of my uploaded files. Emulator didn't fail to upload though. Corrupted files were 1k larger that original ones. Then I set the buffer of the output stream to 1 byte and it began to work with no problem. Finally I let it be 8 bytes and haven't had no more corruption problems. A buffer of about 80 or 50, I can't remember, also failed. Don't understand what the problem is but I'm happy it is working this way. This page was so inspiring thanks.

Turbini
  • 51
  • 3
1

ok. spent 2 days on testing this issue and found out following:
when using tcpdump on android it comes that the data wasn't corrupt, BUT the tcp packet size was 1516, which is very strange, cause the normal ethernet packet size is 1500 and everything more than 1516 is way too big.
i manually changed the MTU to 576 (i believe it is the standard for ppp, which 3G actually is) and it works perfect! 150 of 150 images were uploaded normally!

this doesn't solve the actual problem, though, cause it's impossible to change mtu on non-rooted devices i believe, and you have to change it every time you reboot the device (or every time you bring up the interface - not sure about it, cause couldn't find the way of getting MTU value via ifconfig). but at least i know where the problem is.
setting http chunk size to a smaller value (tried 300 bytes) didn't affect it (i believe it's because the http headers are way too big themselves)... so... so nothing =)

will try to post it to android-developer group on google, but their moderation is way too slow... we'll see...

Alex
  • 536
  • 1
  • 5
  • 13
  • Hi Alex, I think I'm experiencing a similar issue as you but I haven't verified with tcpdump like you did. I'm using httpclient and what i've done is set a checksum on the data the client is sending, on the server side the checksum is wrong on the occasions that we experience this issue. in my case the uploaded data seems to simply missing parts of the file. other times everything works perfectly. i saw your thread on android-developers but it didn't look like you found a solution. anything new to report (oh please!) – dweebo Feb 25 '11 at 17:00
  • if you are missing parts of the file, then this is another problem. if you use mediastore, you should look into froyo bug with wrong file length. cause if the length of the file on the server is different, but it still receives the file, it means that content-length is wrong, other way it would just refuse the upload due to different file length... – Alex Feb 28 '11 at 08:19
  • Thanks Alex, we have verified that the data created by the app is correct, but the version we receive on the server is incorrect. the file size IS different. we aren't using mediastore so the file-size froyo bug is not our problem, it's something else. verified that this only happens with Android 2.2+, but on a range of phones and carriers and it's happening to hundreds of our users – dweebo Mar 14 '11 at 15:04
  • i have a strong feeling that this isn't an android issue. how do you check that the data on android is correct? run a tcpdump to see what exactly goes from the device. there may be 3 issues on this: 1) the connection is accidently broken in the middle of the process. in this case you will hace truncated data. means there will be no difference between same blocks of data on server and client. 2) data may be transferred in not binary mode.. 3) your IO algo on android is inaccurate. the best is to check for tcpdump. only this way you will be 100% sure. you can use wireshark to parse the dump – Alex Mar 15 '11 at 07:42
  • Thanks for your help Alex, since this probably is a different issue than you were having. I agree it doesn't seem like it would be an android issue since no one else in the universe seems to have it happen. we verified the files are correct in the app by having a few users with the problem email us the data files that are sent by the app and we checked them over and they are in the correct format, whereas the versions we get on our server are different and missing random parts of the file, but the parts missing are in the MIDDLE of the file, it's not just a truncation issue. – dweebo Mar 15 '11 at 18:47
  • I started a separate question for my issue http://stackoverflow.com/questions/5316420/httppost-fileupload-data-loss – dweebo Mar 16 '11 at 14:22