<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">之前做的一个图片上传的小模块, 就是从本地文件或者拍照获取之后按一定比例裁剪之后上传</span>
第一次做的时候真是各种伤不起啊, 最恶心的是OOM了, 还有htc手机上面自动人脸识别覆盖自定裁剪尺寸之类一系列奇葩的问题, 以及以及base64+json上传大图片无响应问题...最近重构的时候仔细学习了一下, 之前的几个问题大概有了答案, 记录一下几个问题的解决方案, 后面放实例:
最基本的, 无论是浏览本地图片还是调用相机拍照都是通过activity的:
<span style="white-space:pre"> </span>case R.id.actionbar_camera:
tempUri = Uri.fromFile(tempFile);
intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, tempUri);
startActivityForResult(intent, REQUEST_CAMERA);
break;
case R.id.actionbar_gallery:
intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, REQUEST_GALLERY);
break;
REQUEST_XXX是自己定义的常量, 用于在onActivityResult里面判断, 这就不多说了
这里包括之后会遇到第一个问题OOM OutOfMemory, 这个我就不细说了, 我也是看CSDN上面一篇很强大的博客才弄明白的, 总的来说就是activity之间不要传递bitmap, 放内存里基本上要爆, 这里用Uri, 统一资源标识, 你可以Uri.fromFile为图片准备一个文件地址, 这样你只要掌握这个标识就可以随意访问而不是放在内存里, 这样实在是太蠢了, 亏我之前还花了好大功夫研究怎么压缩bitmap文件...
ContentResolver resolver = getContentResolver();
switch (requestCode) {
case REQUEST_CAMERA:
if (!tempFile.exists())
return;
refresh();
break;
case REQUEST_GALLERY:
if (data == null)
return;
tempUri = data.getData();
if (tempUri == null)
return;
try {
InputStream is = resolver.openInputStream(tempUri);
FileOutputStream os = new FileOutputStream(tempFile);
if (is == null)
return;
int bytesRead;
byte[] buffer = new byte[4 * 1024];
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.flush();
os.close();
is.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
refresh();
break;
如果想浏览本地图片的同时建立一个备份, 就在onActivityResult里面把获取到的本地图片Uri变成file, 简单文件操作啦啦啦
调用裁剪也是利用activity:
tempUri = Uri.fromFile(tempFile);
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(tempUri, "image/*");
intent.putExtra("crop", "true");
intent.putExtra("aspectX", aspectX);
intent.putExtra("aspectY", aspectY);
intent.putExtra("outputX", outputX);
intent.putExtra("outputY", outputY);
intent.putExtra("noFaceDetection", true);
intent.putExtra("return-data", false);
intent.putExtra(MediaStore.EXTRA_OUTPUT, tempUri);
intent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString());
startActivityForResult(intent, REQUEST_CROP);
注意返回值"return-data" 设为false, 如果你不想把裁剪结果按照bitmap返回的话, 用uri多好, 还不会超内存
至此, 图片调用以及裁剪已经没问题了, 裁剪部分csdn有一篇很强大的博客可以找找看, 注意这里一个很恶心的属性"noFaceDetection", 有些手机在裁剪的时候因为检测到人脸, 你的裁剪框会变成正方形, 把这个值设为false就没问题了
下面再说一下图片上传, 先吐槽base64+json, 我之前没经验啦, 上传图片还是用的上传icon的方式...后来有人告诉我base64太脆了...而且我之前上传的时候, 图片稍微大一点就会导致服务器没有响应, 据说是json上传数据量有上限, 超过这个值就呵呵了, 所以最后还是乖乖模拟表单...
我把这个工具代码贡献出来了奥, 其实主体还是从网上扒的一个代码, 忘记来源了不好意思哈, 因为用asynctask比较简单, 而且因为我这边处于接口设计的考虑包装了一下返回值, 以及其他一些原因, 把这个代码改了一下, 其实改过之后就发现其实根本不难啊, 上代码:
这里用两个map分别放params还有文件, urls是服务器返回的图片链接
public class UploadUtil {
public static final int UPLOAD_SUCCESS = 1;
public static final int UPLOAD_FILE_NOT_EXISTS = 2;
public static final int UPLOAD_SERVER_ERROR = 3;
protected static final int WHAT_TO_UPLOAD = 1;
protected static final int WHAT_UPLOAD_DONE = 2;
private static final String BOUNDARY = UUID.randomUUID().toString(); // 边界标识 随机生成
private static final String PREFIX = "--";
private static final String LINE_END = "\r\n";
private static final String CONTENT_TYPE = "multipart/form-data"; // 内容类型
private static final String TAG = "UploadUtil";
private static final String CHARSET = "utf-8"; // 设置编码
private static UploadUtil uploadUtil;
private static int requestTime = 0;
private int readTimeOut = 30 * 1000;
private int connectTimeOut = 30 * 1000;
private OnUploadProcessListener onUploadProcessListener;
private UploadUtil() {
}
public static UploadUtil getInstance() {
if (null == uploadUtil) {
uploadUtil = new UploadUtil();
}
return uploadUtil;
}
public static int getRequestTime() {
return requestTime;
}
public BaseResponse uploadFile(final String requestUrl, Map<String, String> paramMap, Map<String, String> files, Map<String, String> urls) {
Map<String, File> fileMap = new HashMap<String, File>();
for (String key : files.keySet()) {
File tempFile = new File(files.get(key));
if (!tempFile.exists()) {
Log.d(TAG, "no exist 3");
return new BaseResponse(false, "文件不存在");
}
fileMap.put(key, tempFile);
}
requestTime = 0;
long requestTime = System.currentTimeMillis();
long responseTime;
try {
URL url = new URL(requestUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setReadTimeout(readTimeOut);
connection.setConnectTimeout(connectTimeOut);
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setRequestMethod("POST");
connection.setRequestProperty("Charset", CHARSET);
connection.setRequestProperty("connection", "keep-alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
connection.setRequestProperty("Content-Type", CONTENT_TYPE + ";boundary=" + BOUNDARY);
DataOutputStream dos = new DataOutputStream(connection.getOutputStream());
StringBuffer sb;
String param;
for (String key : paramMap.keySet()) {
sb = new StringBuffer();
sb.append(PREFIX).append(BOUNDARY).append(LINE_END);
sb.append("Content-Disposition: form-data; name=\"").append(key).append("\"").append(LINE_END).append(LINE_END);
sb.append(paramMap.get(key)).append(LINE_END);
param = sb.toString();
Log.d(TAG, "param: " + key + " == " + param);
dos.write(param.getBytes());
}
for (String key : fileMap.keySet()) {
sb = new StringBuffer();
sb.append(PREFIX).append(BOUNDARY).append(LINE_END);
sb.append("Content-Disposition:form-data; name=\"").append(key).append("\"; filename=\"")
.append(fileMap.get(key).getName()).append("\"").append(LINE_END);
sb.append("Content-Type:image/pjpeg" + LINE_END);
sb.append(LINE_END);
param = sb.toString();
Log.d(TAG, fileMap.get(key).getName() + " == " + param);
dos.write(param.getBytes());
InputStream is = new FileInputStream(fileMap.get(key));
onUploadProcessListener.initUpload((int) fileMap.get(key).length());
byte[] bytes = new byte[4 * 1024];
int length, curLength = 0;
while ((length = is.read(bytes)) != -1) {
curLength += length;
dos.write(bytes, 0, length);
onUploadProcessListener.onUploadProcess(curLength);
}
is.close();
dos.write(LINE_END.getBytes());
}
byte[] endData = (PREFIX + BOUNDARY + PREFIX + LINE_END).getBytes();
dos.write(endData);
dos.flush();
dos.close();
responseTime = System.currentTimeMillis();
UploadUtil.requestTime = (int) (responseTime - requestTime) / 1000;
InputStream is = connection.getInputStream();
sb = new StringBuffer();
int c;
while ((c = is.read()) != -1) {
sb.append((char) c);
}
is.close();
String entity = sb.toString();
Log.d(TAG, "entity is " + entity);
if (connection.getResponseCode() == 200) {
Log.d(TAG, "request success");
JSONObject object = new JSONObject(entity);
int success = JSONUtil.getInt(object, "success", 0);
String message = JSONUtil.getString(object, "message", "网络连接失败, 请检查网络设置");
if (success != 0) {
Iterator<String> it = object.keys();
while (it.hasNext()) {
String key = it.next();
urls.put(key, JSONUtil.getString(object, key, ""));
}
}
return new BaseResponse(success != 0, message);
} else {
Log.d(TAG, "request error");
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return new BaseResponse(false, "网络连接失败, 请检查网络设置");
}
private void sendMessage(int responseCode, String responseMessage) {
onUploadProcessListener.onUploadDone(responseCode, responseMessage);
}
public void setOnUploadProcessListener(OnUploadProcessListener onUploadProcessListener) {
this.onUploadProcessListener = onUploadProcessListener;
}
public int getReadTimeOut() {
return readTimeOut;
}
public void setReadTimeOut(int readTimeOut) {
this.readTimeOut = readTimeOut;
}
public int getConnectTimeOut() {
return connectTimeOut;
}
public void setConnectTimeOut(int connectTimeOut) {
this.connectTimeOut = connectTimeOut;
}
public static interface OnUploadProcessListener {
void onUploadDone(int responseCode, String message);
void onUploadProcess(int uploadSize);
void initUpload(int fileSize);
}
}