(转)Android学习-使用Async-Http实现图片压缩并上传功能
文章转载自:
作者:RyaneLee
链接:http://www.jianshu.com/p/940fc7ba39e1
让我头疼一个星期的图片批量上传服务器的问题最后受这篇文章的作者启发而解决,自己之前一直执着于通过uri地址找到图片然后上传图片,却没想过直接上传图片本身。感谢作者的博客和启发。
前言
(转载请注明出处,谢谢!)
最近在做一个小项目,项目中要实现上传图片到服务器,而这个例子是实现图片的尺寸压缩,将获取到的压缩图片转为流,然后使用Async-Http开源框架实现图片流的上传,然后在服务器端将流写入本地。
效果图
安卓端
服务器端
解决
安卓端
一、点击加载本地相册选择图片,然后加载到ImageView中去,并获取图片地址以供后面图片上传时使用。
//点击图片打开选择图片界面
photo.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
//使用Intent触发选择Action
Intent intent = new Intent(Intent.ACTION_PICK, null);
//打开系统提供的图片选择界面
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
//传参以在返回本界面时触发加载图片的功能
startActivityForResult(intent, 0x1);
}
});
//在选择图片后返回本界面调用此方法
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 0x1 && resultCode == RESULT_OK) {
if (data != null) { ContentResolver resolver = getContentResolver();
try {
// 获取圖片URI
Uri uri = data.getData();
// 将URI转换为路径:
String[] proj = { MediaStore.Images.Media.DATA };
Cursor cursor = managedQuery(uri, proj, null, null, null);
// 这个是获得用户选择的图片的索引值
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
// 最后根据索引值获取图片路径
photoPath = cursor.getString(column_index); // 压缩成800*480
bitmap = BitmapUtils.decodeSampledBitmapFromFd(photoPath, 480, 800);
// 设置imageview显示图片
photo.setImageBitmap(bitmap);
// 设置textview显示路径
path.setText(photoPath);
} catch (Exception e) {
e.printStackTrace();
} }
}
}
二、图片压缩。在我们需要上传图片时,我们不可能直接把原图上传到服务器,因为一般图片的大小都超过3,4M,所以我们在上传之前需要对图片进行压缩,我是把图片压缩放到了一个工具类,主要是对图片进行尺寸压缩。
public class BitmapUtils { private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
//首先获取原图高度和宽度的一半
final int halfHeight = height / 2;
final int halfWidth = width / 2;
//循环,如果halfHeight和halfWidth同时大于最小宽度和最小高度时,inSampleSize乘2,
//最后得到的宽或者高都是最接近最小宽度或者最小高度的
while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
} /**
* 根据Resources压缩图片
*
* @param res
* @param resId
* @param reqWidth
* @param reqHeight
* @return
*/
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
Bitmap src = BitmapFactory.decodeResource(res, resId, options);
return src;
} /**
* 根据地址压缩图片
*
* @param pathName
* @param reqWidth 最小宽度
* @param reqHeight 最小高度
* @return
*/
public static Bitmap decodeSampledBitmapFromFd(String pathName, int reqWidth, int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
// 若要对图片进行压缩,必须先设置Option的inJustDecodeBounds为true才能进行Option的修改
options.inJustDecodeBounds = true;
// 第一次decodeFile是获取到options.outHeight和options.outWidth
BitmapFactory.decodeFile(pathName, options);
// options.inSampleSize是图片的压缩比,例如原来大小是100*100,options.inSampleSize为1,则不变,
// options.inSampleSize为2,则压缩成50*50
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// 重新设置options.inJustDecodeBounds为false,不能修改option
options.inJustDecodeBounds = false;
// 根据options重新加载图片
Bitmap src = BitmapFactory.decodeFile(pathName, options);
return src;
}
}
三、将Bitmap转为String,同时使用BASE64加密,使传输更加安全,这是我的工具类,里面再一次进行压缩是质量压缩,压缩比例是30%,具体自己调
public class Utils {
// 将bitmap转成string类型通过Base64
public static String BitmapToString(Bitmap bitmap) { ByteArrayOutputStream bao = new ByteArrayOutputStream();
// 将bitmap压缩成30%
bitmap.compress(Bitmap.CompressFormat.JPEG, 30, bao);
// 将bitmap转化为一个byte数组
byte[] bs = bao.toByteArray();
// 将byte数组用BASE64加密
String photoStr = Base64.encodeToString(bs, Base64.DEFAULT);
// 返回String
return photoStr;
}
}
四、通过Async-Http实现图片上传
public void upload(String url) {
// 将bitmap转为string,并使用BASE64加密
String photo = Utils.BitmapToString(bitmap);
// 获取到图片的名字
String name = photoPath.substring(photoPath.lastIndexOf("/")).substring(1);
// new一个请求参数
RequestParams params = new RequestParams();
// 将图片和名字添加到参数中
params.put("photo", photo);
params.put("name", name);
AsyncHttpClient client = new AsyncHttpClient();
// 调用AsyncHttpClient的post方法
client.post(url, params, new AsyncHttpResponseHandler() { @Override
public void onFailure(int arg0, Header[] arg1, byte[] arg2, Throwable arg3) {
Toast.makeText(getApplicationContext(), "上传失败!", Toast.LENGTH_SHORT).show();
} @Override
public void onSuccess(int arg0, Header[] arg1, byte[] arg2) {
Toast.makeText(getApplicationContext(), "上传成功!", Toast.LENGTH_SHORT).show();
}
});
}
服务器端
一、创建一个新的Servlet,在doget方法里面实现
public class UpLoadPhotoServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html");
// 获取文件名
String name = request.getParameter("name");
// 获取图片
String photo = request.getParameter("photo");
// 将传进来的图片的string格式进行处理
byte[] bs = new BASE64Decoder().decodeBuffer(photo);
// 写到E盘Img文件夹下的a.jpg文件。注:Img文件夹一定要存在
FileOutputStream fos = new FileOutputStream("E:/Img/" + name);
fos.write(bs);
fos.flush();
fos.close(); PrintWriter writer = response.getWriter();
writer.print("上传成功");
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}