存档

文章标签 ‘imgly’

折腾记录:Android上增加img.ly上传

2010年5月6日 没有评论

好久没有更新了,一直在做我的Android程序,但目前看来一时半会儿做不完,还是调整一下节奏,也把做的过程中遇到的问题记录一下。

昨天,打算把我的程序中加入拍照并上传到img.ly的功能,遇到了一串问题。

问题一:Android下调用拍照Activity

通过MediaStore.ACTION_IMAGE_CAPTURE这个Intent可以使用系统提供的拍照功能,但用了之后,发现拍摄的照片相当小,即使设了MediaStore.EXTRA_OUTPUT,也是没有作用。

Google之后,发现这是个已知的问题:http://code.google.com/p/android/issues/detail?id=1480,唉,郁闷。

看了几个其他Android上支持拍照的程序,他们也只能拍小图片。算了,小就小吧,还省流量呢,哈哈

问题二:上传代码

查阅img.ly的API文档,真是简单,只有一页。里面说格式为multipart/form-data,要求三项:media, username, password。

找遍了Android文档,没有发现支持multipart/form-data的Http相关函数。Google之,org.apache.http新版本中包含了对Multipart的支持,但Android中带的org.apache.http没有。一个Android Team的回答

Note that this has been removed because it was removed from the Apache
HttpClient library that we’re bundling. What you want to do is get
Mime4j ( http://james.apache.org/mime4j/index.html ) and HttpMime
( http://hc.apache.org/httpcomponents-client/httpmime/index.html ) and
include these libraries in your Android project. From there, the usage
of multipart requests is pretty intuitive.

按照他的说法,下载了mime4j和httpmime,终于可以支持multipart/form-data了。

问题三:HTTP Error 411 Length required

按照img.ly的API,在post中加入了media, username, password,运行。得到一个Exception,因为img.ly返回HTTP Error 411 Length required。

抓包,看到发出的http请求里,的却没有Content-length。尝试直接通过代码加入Content-length,未果,还触发异常。

Google之,找到了原因:我针对media这个字段,使用的是InputStreamBody,这个Body的getContentLength()返回-1,造成不能计算总的长度,所以http header里没有Content-length。

这个帖子里给出了一个解决方法,自己写了一个ByteBody。这个代码是08年的,可能是针对以前的版本,我照猫画虎,做了一个:

import java.io.IOException;
import java.io.OutputStream;

import org.apache.http.entity.mime.MIME;
import org.apache.http.entity.mime.content.AbstractContentBody;

public class ByteBody extends AbstractContentBody {
	private final byte[] mContent;

	public ByteBody(final byte[] content, final String mimeType) {
		super(mimeType);
		if (content==null) {
			throw new IllegalArgumentException("File may not be null");
		}
		this.mContent = content;
	}

	@Override
	public void writeTo(OutputStream out) throws IOException {
		if (out == null) {
            throw new IllegalArgumentException("Output stream may not be null");
        }
        try {
            out.write(mContent, 0, mContent.length);
            out.flush();
        } finally {
        }
	}

	@Override
	public String getFilename() {
		return("bytearray");
	}

	@Override
	public String getCharset() {
		return null;
	}

	@Override
	public long getContentLength() {
		return mContent.length;
	}

	@Override
	public String getTransferEncoding() {
		return MIME.ENC_BINARY;
	}

}

运行,终于HTTP 411不出现了,但又来了新的问题……

问题四:Invalid twitter username or password

这个提示很奇怪,明明我提供的是正常的。联想到在使用MultipartEntity之前,我曾经用直接写代码的方式实现post请求,没有出现这个问题,是不是MultipartEntity发送的格式img.ly不接受,造成不能识别username/password。

MultipartEntiry的发送格式是:

Content-Disposition: form-data; name=”username”
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

suzginfo

找了一个可以上传到img.ly的程序,抓包看它的发送格式 —— 它竟然用https发送,看不到发送内容!

不过,我又发现,它的logcat里,有发送内容,太好了

Content-Disposition: form-data; name=”username”

suzginfo

和我的差别是没有Content-Type和Content-Transfer-Encoding。

看文档和源文件,MultipartEntity是有两个模式,STRICT和BROWSER_COMPATIBLE,缺省的是STRICT。在STRICT下,会发送Content-Type和Content-Transfer-Encoding。而BROWSER_COMPATIBLE不会发送。

换成了BROWSER_COMPATIBLE模式,Invalid twitter username or password消失了,错误变成了Invalid image type。看来对于media字段来说,是需要Content-Type的。这个在分析另一个程序的logcat中,也看到了它发送Content-Type和Content-Transfer-Encoding。

这个就麻烦了,我需要STRICT和BROWSER_COMPATIBLE的组合模式,对username和password不发送Content-Type,而media发送。

没办法,重写MultipartEntity和HttpMultipart。在HttpMultipart.doWriteTo里,如果Content-Type是text/plain,就跳过,直接发送内容。

修改后,终于可以上传图片了,内牛满面啊~

分类: Android 标签: , ,