2

I want to implement a ProgressDialog in my AndroidHttpClient. I found a simple implementation here CountingMultipartEntity.
Additional I added the content length support. I override the method addPart.
The FileBody upload works almost fine. When the upload contains one file it works perfect, but when there are two files the second file is only uploaded partial.
The InputStreamBody works but only when I don't count the length of the InputStream. So I have to reset it, but how?

Here my overriding addPart:

@Override
public void addPart(String name, ContentBody cb) {
    if (cb instanceof FileBody) {
        this.contentLength += ((FileBody) cb).getFile().length();
    } else if (cb instanceof InputStreamBody) {
        try {
            CountingInputStream in =
                new CountingInputStream(((InputStreamBody) cb).getInputStream());
            ObjectInputStream ois = new ObjectInputStream(in);
            ois.readObject();
            this.contentLength += in.getBytesRead();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    super.addPart(name, cb);
}

The CountingInputStream is a simple extension of the InputStream:

public class CountingInputStream extends InputStream {
    private InputStream source;
    private long bytesRead = 0;

    public CountingInputStream(InputStream source) {
        this.source = source;
    }

    public int read() throws IOException {
        int value = source.read();
        bytesRead++;
        return value;
    }

    public long getBytesRead() {
        return bytesRead;
    }
}

The counting works almost, there are only 2 bytes, which shouldn't be there. But that is so important.

First I thought the stream must be reseted. The reset called after in.getReadedBytes(); leads into an IOException.

Thanks for any advices.

Community
  • 1
  • 1
CSchulz
  • 10,882
  • 11
  • 60
  • 114
  • 1
    Did you mean to preserve that bare `in.getReadedBytes()` call? Note that you're calling it twice. – seh Jun 17 '11 at 21:33
  • Thanks for the advice. Now I have 1 byte too much. – CSchulz Jun 17 '11 at 21:49
  • 2
    Note that you bump the count even when `InputStream#read()` returns -1, which indicates *eos*. You should not count the last read, as it's just returning the sentinel value, not a byte from the actual stream content. – seh Jun 17 '11 at 23:14
  • Thanks for your hit. I have just updated the sourcecode. Now the file get partially uploaded but there is a *StreamCorruptedException* at `ObjectInputStream ois = new ObjectInputStream(in);`. – CSchulz Jun 17 '11 at 23:32
  • That update to `read()` still looks wrong. It's hard to write in a comment, but try this instead:`final int value = source.read(); if (-1 != value) ++bytesRead; return value;` – seh Jun 17 '11 at 23:37
  • Thanks, but same result as the method `read()` above. – CSchulz Jun 17 '11 at 23:43
  • `StreamCorruptionException` indicates that the magic header is corrupt, per `ObjectInputStream#readStreamHeader()`. Perhaps the part you're reading was not written by `ObjectOutputStream`. Try dumping the first four bytes out. You're looking for `0xACED 0x0005`. What did the exception message indicate as the four bytes it actually found? – seh Jun 18 '11 at 00:17
  • Oh ... After I have changed to the *ObjectInputStream* the server received bytes from the *InputStream* (2 bytes missing). The object is written with a *FileOutputStream*, which write a byte array. The exception doesn't contain any information about the four bytes. – CSchulz Jun 18 '11 at 00:35
  • I checked what exactly is transmitted to server. There are missing the first bytes of the file. – CSchulz Jun 18 '11 at 13:58

1 Answers1

1

I found my mistake. I had overwritten the method getContentLength(), which is important for the transmission, after removing my own version the file transmission works fine.

To get the size of an InputStream I used the class above but edited the method getBytesRead(), because the ObjectInputStream causes a StreamCorruptedException:

public long getBytesRead() {
    try {
        while (read() != -1)
            ;
    } catch (IOException e) {
        e.printStackTrace();
    }

    return bytesRead;
}

To get the content length, you can take the given method getContentLength() if there aren't any streams.
Otherwise you have to implement your own content length calculation. The method addPart(String name, ContentBody cb) above provides an approach. More details about the content length calculation you can get from the classes MultiPartyEntity and HttpMultipart.

CSchulz
  • 10,882
  • 11
  • 60
  • 114