最近遇到需求是前后端传输图片使用的是Base64,但是前端(Android 端)图片很大(尺寸很大4480 × 2520,质量也很大7-10M),需要压缩到一定尺寸(1280 × 960,当然还可以压得更小),然后传给后端,本来认为是一个简单的压缩,可是在子线程压缩过程中发现很卡,卡的主线程的进度条都走不动了,一开始用的模拟器测试的,以为模拟器性能比较差,可是换成手机测试,还是很卡,卡的主线程的进度条都走不动了。为了解决这个问题,请看以下步骤:
以下代码在项目里,项目地址:https://gitee.com/urasaki/RxJava2AndRetrofit2
1.先申请权限,关于如何申请权限请查看RxPermissions的使用(简单实用)_ErwinNakajima的博客-CSDN博客_rxpermissions
2.添加bitmap管理类。
package com.phone.base64_and_file.manager;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ImageFormat;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.YuvImage;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import com.phone.common_library.manager.LogManager;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.nio.ByteBuffer;
import java.text.DecimalFormat;
import id.zelory.compressor.Compressor;
public class BitmapManager {
private static final String TAG = "BitmapManager";
/**
* 图像的旋转方向是0
*/
public static final int ROTATE0 = 0;
/**
* 图像的旋转方向是90
*/
public static final int ROTATE90 = 90;
/**
* 图像的旋转方向是180
*/
public static final int ROTATE180 = 180;
/**
* 图像的旋转方向是270
*/
public static final int ROTATE270 = 270;
/**
* 图像的旋转方向是360
*/
public static final int ROTATE360 = 360;
/**
* 图片太大内存溢出后压缩的比例
*/
public static final int PIC_COMPRESS_SIZE = 4;
/**
* 图像压缩边界
*/
public static final int IMAGEBOUND = 128;
/**
* 图片显示最大边的像素
*/
public static final int MAXLENTH = 1024;
/**
* Log switch
*/
private static final boolean DEBUG = false;
/**
* 保存图片的质量:100
*/
private static final int QUALITY = 100;
/**
* 默认的最大尺寸
*/
private static final int DEFAULT_MAX_SIZE_CELL_NETWORK = 600;
/**
* 题编辑wifi环境下压缩的最大尺寸
*/
private static final int QUESTION_MAX_SIZE_CELL_NETWORK = 1024;
/**
* 图片压缩的质量
*/
private static final int QUESTION_IMAGE_JPG_QUALITY = 75;
/**
* 默认的图片压缩的质量
*/
private static final int DEFAULT_IMAGE_JPG_QUALITY = 50;
/**
* 网络请求超时时间
*/
private static final int CONNECTTIMEOUT = 3000;
/**
* Private constructor to prohibit nonsense instance creation.
*/
private BitmapManager() {
}
/**
* 图片合成
*
* @param bitmap 位图1
* @param mark 位图2
* @return Bitmap
*/
public static Bitmap createBitmap(Bitmap bitmap, Bitmap mark) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
int mW = mark.getWidth();
int mH = mark.getHeight();
Bitmap newbitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);// 创建一个长宽一样的位图
Canvas cv = new Canvas(newbitmap);
cv.drawBitmap(bitmap, 0, 0, null);// 在 0,0坐标开始画入bitmap
cv.drawBitmap(mark, w - mW, h - mH, null);// 在右下角画入水印mark
cv.save();// 保存
cv.restore();// 存储
return newbitmap;
}
/**
* 放大缩小图片
*
* @param bitmap 位图
* @param w 新的宽度
* @param h 新的高度
* @return Bitmap
*/
public static Bitmap zoomBitmap(Bitmap bitmap, int w, int h) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Matrix matrix = new Matrix();
float scaleWidht = ((float) w / width);
float scaleHeight = ((float) h / height);
matrix.postScale(scaleWidht, scaleHeight);
return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
}
/**
* 旋转图片
*
* @param bitmap 要旋转的图片
* @param angle 旋转角度
* @return bitmap
*/
public static Bitmap rotate(Bitmap bitmap, int angle) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
}
/**
* 圆形图片
*
* @param source 位图
* @param strokeWidth 裁剪范围 0表示最大
* @param bl 是否需要描边
* @param bl 画笔粗细
* @param bl 颜色代码
* @return bitmap
*/
public static Bitmap createCircleBitmap(Bitmap source, int strokeWidth, boolean bl, int edge, int color) {
int diameter = source.getWidth() < source.getHeight() ? source.getWidth() : source.getHeight();
Bitmap target = Bitmap.createBitmap(diameter, diameter, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(target);//创建画布
Paint paint = new Paint();
paint.setAntiAlias(true);
canvas.drawCircle(diameter / 2, diameter / 2, diameter / 2, paint);//绘制圆形
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//取相交裁剪
canvas.drawBitmap(source, strokeWidth, strokeWidth, paint);
if (bl) {
if (color == 0) color = 0xFFFEA248;//默认橘黄色
paint.setColor(color);
paint.setStyle(Paint.Style.STROKE);//描边
paint.setStrokeWidth(edge);
canvas.drawCircle(diameter / 2, diameter / 2, diameter / 2, paint);
}
return target;
}
/**
* 圆角图片
*
* @param bitmap 位图
* @param rx x方向上的圆角半径
* @param ry y方向上的圆角半径
* @param bl 是否需要描边
* @param bl 画笔粗细
* @param bl 颜色代码
* @return bitmap
*/
public static Bitmap createCornerBitmap(Bitmap bitmap, int rx, int ry, boolean bl, int edge, int color) {
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);//创建画布
Paint paint = new Paint();
paint.setAntiAlias(true);
Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
RectF rectF = new RectF(rect);
canvas.drawRoundRect(rectF, rx, ry, paint);//绘制圆角矩形
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//取相交裁剪
canvas.drawBitmap(bitmap, rect, rect, paint);
if (bl) {
if (color == 0) color = 0xFFFEA248;//默认橘黄色
paint.setColor(color);
paint.setColor(color);
paint.setStyle(Paint.Style.STROKE);//描边
paint.setStrokeWidth(edge);
canvas.drawRoundRect(rectF, rx, ry, paint);
}
return output;
}
/**
* 按比例裁剪图片
*
* @param bitmap 位图
* @param wScale 裁剪宽 0~100%
* @param hScale 裁剪高 0~100%
* @return bitmap
*/
public static Bitmap cropBitmap(Bitmap bitmap, float wScale, float hScale) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
int wh = (int) (w * wScale);
int hw = (int) (h * hScale);
int retX = (int) (w * (1 - wScale) / 2);
int retY = (int) (h * (1 - hScale) / 2);
return Bitmap.createBitmap(bitmap, retX, retY, wh, hw, null, false);
}
/**
* 获得带倒影的图片方法
*
* @param bitmap 位图
* @param region 倒影区域 0.1~1
* @return bitmap
*/
public static Bitmap createReflectionBitmap(Bitmap bitmap, float region) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Matrix matrix = new Matrix();
matrix.preScale(1, -1);//镜像缩放
Bitmap reflectionBitmap = Bitmap.createBitmap(
bitmap, 0
, (int) (height * (1 - region))//从哪个点开始绘制
, width
, (int) (height * region)//绘制多高
, matrix, false);
Bitmap reflectionWithBitmap = Bitmap.createBitmap(width, height + (int) (height * region),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(reflectionWithBitmap);
canvas.drawBitmap(bitmap, 0, 0, null);
canvas.drawBitmap(reflectionBitmap, 0, height, null);
LinearGradient shader = new LinearGradient(0, bitmap.getHeight(), 0,
reflectionWithBitmap.getHeight()
, 0x70ffffff, 0x00ffffff, Shader.TileMode.CLAMP);
Paint paint = new Paint();
paint.setShader(shader);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));//取两层绘制交集。显示下层。
canvas.drawRect(0, height, width, reflectionWithBitmap.getHeight(), paint);
return reflectionWithBitmap;
}
/**
* 图片质量压缩
*
* @param bitmap
* @param many 百分比
* @return
*/
public static Bitmap compressBitmap(Bitmap bitmap, float many) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, (int) many * 100, baos);
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
return BitmapFactory.decodeStream(isBm, null, null);
}
/**
* 高级图片质量压缩
*
* @param bitmap 位图
* @param maxSize 压缩后的大小,单位kb
*/
public static Bitmap imageZoom(Bitmap bitmap, double maxSize) {
// 将bitmap放至数组中,意在获得bitmap的大小(与实际读取的原文件要大)
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 格式、质量、输出流
bitmap.compress(Bitmap.CompressFormat.PNG, 70, baos);
byte[] b = baos.toByteArray();
// 将字节换成KB
double mid = b.length / 1024;
// 获取bitmap大小 是允许最大大小的多少倍
double i = mid / maxSize;
// 判断bitmap占用空间是否大于允许最大空间 如果大于则压缩 小于则不压缩
doRecycledIfNot(bitmap);
if (i > 1) {
// 缩放图片 此处用到平方根 将宽带和高度压缩掉对应的平方根倍
// (保持宽高不变,缩放后也达到了最大占用空间的大小)
return scaleWithWH(bitmap, bitmap.getWidth() / Math.sqrt(i),
bitmap.getHeight() / Math.sqrt(i));
}
return null;
}
/***
* 图片缩放
*@param bitmap 位图
* @param w 新的宽度
* @param h 新的高度
* @return Bitmap
*/
public static Bitmap scaleWithWH(Bitmap bitmap, double w, double h) {
if (w == 0 || h == 0 || bitmap == null) {
return bitmap;
} else {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Matrix matrix = new Matrix();
float scaleWidth = (float) (w / width);
float scaleHeight = (float) (h / height);
matrix.postScale(scaleWidth, scaleHeight);
return Bitmap.createBitmap(bitmap, 0, 0, width, height,
matrix, true);
}
}
/**
* YUV视频流格式转bitmap
*
* @param data YUV视频流格式
* @return width 设置高度
*/
public static Bitmap getBitmap(byte[] data, int width, int height) {
Bitmap bitmap;
YuvImage yuvimage = new YuvImage(data, ImageFormat.NV21, width, height, null);
//data是onPreviewFrame参数提供
ByteArrayOutputStream baos = new ByteArrayOutputStream();
yuvimage.compressToJpeg(new Rect(0, 0, yuvimage.getWidth(), yuvimage.getHeight()), 100, baos);//
// 80--JPG图片的质量[0-100],100最高
byte[] rawImage = baos.toByteArray();
BitmapFactory.Options options = new BitmapFactory.Options();
SoftReference<Bitmap> softRef = new SoftReference<Bitmap>(BitmapFactory.decodeByteArray(rawImage, 0, rawImage
.length, options));
bitmap = softRef.get();
return bitmap;
}
/**
* 图片资源文件转bitmap
*
* @param context
* @param resId
* @return bitmap
*/
public static Bitmap getBitmapResources(Context context, int resId) {
return BitmapFactory.decodeResource(context.getResources(), resId);
}
/**
* 将图片路径转Bitmap
*
* @return Bitmap
* @Param path 图片路径
*/
public static Bitmap getBitmapPath(String path) {
return BitmapFactory.decodeFile(path);
}
/**
* bitmap保存到指定路径
*
* @param path 图片的绝对路径
* @param bmp 位图
* @return bitmap
*/
public static boolean saveFile(String path, Bitmap bmp) {
if (TextUtils.isEmpty(path) || bmp == null) return false;
File file = new File(path);
if (file.exists()) {
file.delete();
} else {
File p = file.getParentFile();
if (!p.exists()) {
p.mkdirs();
}
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
FileOutputStream fos = null;
BufferedOutputStream bos = null;
try {
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
bmp.compress(Bitmap.CompressFormat.JPEG, 100, bos);
bos.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return true;
}
/**
* 回收一个未被回收的Bitmap
*
* @param bitmap
*/
public static void doRecycledIfNot(Bitmap bitmap) {
if (!bitmap.isRecycled()) {
bitmap.recycle();
}
}
/**
* 等比压缩图片
*
* @param resBitmap 原图
* @param desWidth 压缩后图片的宽度
* @param desHeight 压缩后图片的高度
* @return 压缩后的图片
*/
public static Bitmap scaleImage(Bitmap resBitmap, int desWidth, int desHeight) {
int resWidth = resBitmap.getWidth();
int resHeight = resBitmap.getHeight();
if (resHeight > desHeight || resWidth > desWidth) {
// 计算出实际宽高和目标宽高的比率
final float heightRatio = (float) desHeight / (float) resHeight;
final float widthRatio = (float) desWidth / (float) resWidth;
float scale = heightRatio < widthRatio ? heightRatio : widthRatio;
LogManager.i(TAG, "scaleImage scale*****" + scale);
//開平方會有一點損失精度
float prescription = (float) Math.sqrt(scale);
//再次开平方
float prescription2 = (float) Math.sqrt(prescription);
// LogManager.i(TAG, "scaleImage prescription*****" + prescription);
LogManager.i(TAG, "scaleImage prescription2*****" + prescription2);
Bitmap bitmap = null;
//循環壓縮bitmap,防止一次壓縮bitmap卡頓問題(子線程把主線成卡頓了)
for (int i = 0; i < 4; i++) {
if (bitmap != null) {
// bitmap = scale(bitmap, prescription);
bitmap = scale(bitmap, prescription2);
} else {
// bitmap = scale(resBitmap, prescription);
bitmap = scale(resBitmap, prescription2);
}
// LogManager.i(TAG, i + "scaleImage prescription*****" + prescription);
LogManager.i(TAG, i + "scaleImage prescription2*****" + prescription2);
}
return bitmap;
}
return resBitmap;
}
/**
* 等比压缩图片
*
* @param bitmap 原图
* @param scale 压缩因子
* @return 压缩后的图片
*/
private static Bitmap scale(Bitmap bitmap, float scale) {
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
/**
* 尺寸缩放
*
* @param bitmap bitmap
* @param w width
* @param h height
* @return scaleBitmap
*/
public static Bitmap scale(Bitmap bitmap, int w, int h) {
if (bitmap == null) {
return null;
}
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Matrix matrix = new Matrix();
float scaleWidth = ((float) w / width);
float scaleHeight = ((float) h / height);
matrix.postScale(scaleWidth, scaleHeight);
return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
}
/**
* byte转Uri
*
* @param context
* @param data
* @return
*/
public static Uri saveTakePictureImage(Context context, byte[] data) {
File file = context.getExternalFilesDir("file1");
file = new File(file.getAbsolutePath() + File.separator + System.currentTimeMillis() + ".jpg");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
fos.write(data);
fos.flush();
} catch (Exception e) {
e.printStackTrace();
// 异常时删除保存失败的文件
try {
if (file != null && file.exists() && file.isFile()) {
file.delete();
}
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Log.e(TAG, file.getAbsolutePath());
return Uri.fromFile(file);
}
/**
* byte转成bitmap
*
* @param data
* @param width
* @param height
* @return
*/
public static Bitmap yuvToBitmap(byte[] data, int width, int height) {
int frameSize = width * height;
int[] rgba = new int[frameSize];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int y = (0xff & ((int) data[i * width + j]));
int u = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 0]));
int v = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 1]));
y = y < 16 ? 16 : y;
int r = Math.round(1.164f * (y - 16) + 1.596f * (v - 128));
int g = Math.round(1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128));
int b = Math.round(1.164f * (y - 16) + 2.018f * (u - 128));
r = r < 0 ? 0 : (r > 255 ? 255 : r);
g = g < 0 ? 0 : (g > 255 ? 255 : g);
b = b < 0 ? 0 : (b > 255 ? 255 : b);
rgba[i * width + j] = 0xff000000 + (b << 16) + (g << 8) + r;
}
}
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bmp.setPixels(rgba, 0, width, 0, 0, width, height);
return bmp;
}
/**
* byte转成bitmap
*
* @param depthBytes
* @param width
* @param height
* @return
*/
public static Bitmap depthToBitmap(byte[] depthBytes, int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
int[] argbData = new int[width * height];
for (int i = 0; i < width * height; i++) {
argbData[i] = (((int) depthBytes[i * 2] + depthBytes[i * 2 + 1] * 256) / 10 & 0x000000ff)
| ((((int) depthBytes[i * 2] + depthBytes[i * 2 + 1] * 256) / 10) & 0x000000ff) << 8
| ((((int) depthBytes[i * 2] + depthBytes[i * 2 + 1] * 256) / 10) & 0x000000ff) << 16
| 0xff000000;
}
bitmap.setPixels(argbData, 0, width, 0, 0, width, height);
return bitmap;
}
/**
* byte转bitmap
*
* @param bytes
* @param width
* @param height
* @return
*/
public static Bitmap rGBToBitmap(byte[] bytes, int width, int height) {
// use Bitmap.Config.ARGB_8888 instead of type is OK
Bitmap stitchBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
byte[] rgba = new byte[width * height * 4];
for (int i = 0; i < width * height; i++) {
byte b1 = bytes[i * 3 + 0];
byte b2 = bytes[i * 3 + 1];
byte b3 = bytes[i * 3 + 2];
// set value
rgba[i * 4 + 0] = b1;
rgba[i * 4 + 1] = b2;
rgba[i * 4 + 2] = b3;
rgba[i * 4 + 3] = (byte) 255;
}
stitchBmp.copyPixelsFromBuffer(ByteBuffer.wrap(rgba));
return stitchBmp;
}
/**
* byte转bitmap
*
* @param bytes
* @param width
* @param height
* @return
*/
public static Bitmap bGRToBitmap(byte[] bytes, int width, int height) {
// use Bitmap.Config.ARGB_8888 instead of type is OK
Bitmap stitchBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
byte[] rgba = new byte[width * height * 4];
for (int i = 0; i < width * height; i++) {
byte b1 = bytes[i * 3 + 0];
byte b2 = bytes[i * 3 + 1];
byte b3 = bytes[i * 3 + 2];
// set value
rgba[i * 4 + 0] = b3;
rgba[i * 4 + 1] = b2;
rgba[i * 4 + 2] = b1;
rgba[i * 4 + 3] = (byte) 255;
}
stitchBmp.copyPixelsFromBuffer(ByteBuffer.wrap(rgba));
return stitchBmp;
}
public static byte[] aRGBToR(byte[] bytes, int width, int height) {
byte[] IR = new byte[width * height];
for (int i = 0; i < width * height; i++) {
IR[i] = bytes[i * 4];
}
return IR;
}
// /**
// * 百度人脸图片转换
// *
// * @param newInstance
// * @return
// */
// public static Bitmap getInstaceBmp(BDFaceImageInstance newInstance) {
// Bitmap transBmp = null;
// if (newInstance.imageType == BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_RGBA) {
// transBmp = Bitmap.createBitmap(newInstance.width, newInstance.height, Bitmap.Config.ARGB_8888);
// transBmp.copyPixelsFromBuffer(ByteBuffer.wrap(newInstance.data));
// } else if (newInstance.imageType == BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_BGR) {
// transBmp = BitmapManager.BGR2Bitmap(newInstance.data, newInstance.width, newInstance.height);
// } else if (newInstance.imageType == BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_YUV_NV21) {
// transBmp = BitmapManager.yuv2Bitmap(newInstance.data, newInstance.width, newInstance.height);
// } else if (newInstance.imageType == BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_GRAY) {
// transBmp = Depth2Bitmap(newInstance.data, newInstance.width, newInstance.height);
// }
// return transBmp;
// }
/**
* 获取图片数据
*
* @param path
* @return
*/
public static Bitmap getBitmap(String path) {
FileInputStream fis = null;
BufferedInputStream bis = null;
Bitmap bm = null;
try {
fis = new FileInputStream(path);
BitmapFactory.Options options = new BitmapFactory.Options();
// options.inSampleSize = 8;//图片的长宽都是原来的1/8
bis = new BufferedInputStream(fis);
bm = BitmapFactory.decodeStream(bis, null, options);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return bm;
}
/**
* @param bitmap
* @return
*/
public static InputStream bitmapToInputStream(Bitmap bitmap) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
ByteArrayInputStream byteArrInputStream = new ByteArrayInputStream(
baos.toByteArray());
return byteArrInputStream;
}
/**
* 这个是把文件变成二进制流
*
* @param imagepath
* @return
* @throws Exception
*/
public static byte[] readStream(String imagepath) {
FileInputStream fis = null;
BufferedInputStream bis = null;
ByteArrayOutputStream baos = null;
try {
fis = new FileInputStream(imagepath);
bis = new BufferedInputStream(fis);
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while (-1 != (len = bis.read(buffer))) {
baos.write(buffer, 0, len);
}
//刷新此输出流并强制写出所有缓冲的输出字节
baos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bis != null) {
bis.close();
}
if (baos != null) {
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return baos.toByteArray();
}
/**
* 将Bitmap转换成文件
* 保存文件
*
* @param bitmap
* @param fileName
* @throws IOException
*/
public static File saveFile(Bitmap bitmap, String dirsPath, String fileName) {
File dirs = new File(dirsPath);
if (!dirs.exists()) {
dirs.mkdirs();
}
File captureFile = new File(dirs, fileName);
BufferedOutputStream bos = null;
try {
bos = new BufferedOutputStream(new FileOutputStream(captureFile));
//刷新此输出流并强制写出所有缓冲的输出字节
bos.flush();
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return captureFile;
}
/**
* 读取文件的大小
*/
public static long getFileSize(File file) {
long size = 0;
if (file.exists()) {
FileInputStream fis = null;
BufferedInputStream bis = null;
try {
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
size = bis.available();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return size;
}
return 0;
}
/**
* 返回byte的数据大小对应的文本
*
* @param size
* @return
*/
public static String getDataSize(long size) {
DecimalFormat formater = new DecimalFormat("####.00");
if (size < 1024) {
return size + "bytes";
} else if (size < 1024 * 1024) {
float kbsize = size / 1024f;
return formater.format(kbsize) + "KB";
} else if (size < 1024 * 1024 * 1024) {
float mbsize = size / 1024f / 1024f;
return formater.format(mbsize) + "MB";
} else if (size < 1024 * 1024 * 1024 * 1024) {
float gbsize = size / 1024f / 1024f / 1024f;
return formater.format(gbsize) + "GB";
} else {
return "size: error";
}
}
/**
* 使用Compressor IO模式自定义压缩
*
* @param path .setMaxWidth(640).setMaxHeight(480)这两个数值越高,压缩力度越小,图片也不清晰
* .setQuality(75)这个方法只是设置图片质量,并不影响压缩图片的大小KB
* .setCompressFormat(Bitmap.CompressFormat.WEBP) WEBP图片格式是Google推出的 压缩强,质量 高,但是IOS不识别,需要把图片转为字节流然后转PNG格式
* .setCompressFormat(Bitmap.CompressFormat.PNG)PNG格式的压缩,会导致图片变大,并耗过大的内 存,手机反应缓慢
* .setCompressFormat(Bitmap.CompressFormat.JPEG)JPEG压缩;压缩速度比PNG快,质量一般,基本上属于1/10的压缩比例
*/
public static File initCompressorIO(Context context, String path, String dirsPath) {
File dirs2 = new File(dirsPath);
if (!dirs2.exists()) {
dirs2.mkdirs();
}
File file = null;
try {
file = new Compressor(context)
// .setMaxWidth(1280)
// .setMaxHeight(960)
.setQuality(50)
.setCompressFormat(Bitmap.CompressFormat.PNG)
.setDestinationDirectoryPath(dirsPath)
.compressToFile(new File(path));
} catch (IOException e) {
e.printStackTrace();
}
return file;
}
public static File getAssetFile(Context context, String dirsPath, String assetFileName) {
AssetManager assetManager = context.getAssets();
File dirs = new File(dirsPath);
if (!dirs.exists()) {
dirs.mkdirs();
}
File file = new File(dirs, assetFileName);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
InputStream is = null;
BufferedInputStream bis = null;
FileOutputStream fos = null;
BufferedOutputStream bos = null;
try {
is = assetManager.open(assetFileName);
bis = new BufferedInputStream(is);
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
byte[] buffer = new byte[1024];
int len = 0;
while (-1 != (len = bis.read(buffer))) {
bos.write(buffer, 0, len);
}
//刷新此输出流并强制写出所有缓冲的输出字节
bos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return file;
}
/**
* base64转bitmap
*
* @param base64String
* @return
*/
public static Bitmap base64ToBitmap(String base64String) {
base64String = Uri.decode(base64String);
byte[] decode = Base64.decode(base64String, Base64.DEFAULT);
Bitmap bitmap = BitmapFactory.decodeByteArray(decode, 0, decode.length);
return bitmap;
}
}
3.把本地图片转化成bitmap。
String dirsPath = Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator + "Pictures";
String dirsPathCompressed = Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator + "PicturesCompressed";
String dirsPathCompressedRecover = Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator + "PicturesCompressedRecover";
Base64AndFileBean base64AndFileBean = new Base64AndFileBean();
base64AndFileBean.setDirsPath(dirsPath);
base64AndFileBean.setDirsPathCompressed(dirsPathCompressed);
base64AndFileBean.setDirsPathCompressedRecover(dirsPathCompressedRecover);
Bitmap bitmap = BitmapManager.getBitmap(base64AndFileBean.getFile().getAbsolutePath());
LogManager.i(TAG, "bitmap mWidth*****" + bitmap.getWidth());
LogManager.i(TAG, "bitmap mHeight*****" + bitmap.getHeight());
base64AndFileBean.setBitmap(bitmap);
4.再压缩bitmap,循環壓縮bitmap(分成4次压缩了),防止一次壓縮bitmap卡頓問題,如果图片太大,就分成更多次压缩。
Bitmap bitmapCompressed = BitmapManager.scaleImage(base64AndFileBean.getBitmap(), 1280, 960);
LogManager.i(TAG, "bitmapCompressed mWidth*****" + bitmapCompressed.getWidth());
LogManager.i(TAG, "bitmapCompressed mHeight*****" + bitmapCompressed.getHeight());
base64AndFileBean.setBitmapCompressed(bitmapCompressed);
5.再把压缩后的bitmap保存到本地。
File fileCompressed = BitmapManager.saveFile(base64AndFileBean.getBitmapCompressed(), base64AndFileBean.getDirsPathCompressed(), "picture_large_compressed.png");
base64AndFileBean.setFileCompressed(fileCompressed);
LogManager.i(TAG, "base64AndFileBean.getFileCompressed().getPath()*****" + base64AndFileBean.getFileCompressed().getPath());
LogManager.i(TAG, "base64AndFileBean.getFileCompressed().getAbsolutePath()*****" + base64AndFileBean.getFileCompressed().getAbsolutePath());
6.最后把File转成Base64。
String base64Str = null;
if (base64AndFileBean.getFileCompressed().exists()) {
base64Str = Base64AndFileManager.fileToBase64(base64AndFileBean.getFileCompressed());
// base64Str = Base64AndFileManager.fileToBase64Test(file);
// base64Str = Base64AndFileManager.fileToBase64Second(file);
base64AndFileBean.setBase64Str(base64Str);
}
if (!TextUtils.isEmpty(base64Str)) {
String fileName = "base64Str.txt";
String txtFilePath = FileManager.writeStrToTextFile(base64Str, base64AndFileBean.getDirsPathCompressed(), fileName);
base64AndFileBean.setTxtFilePath(txtFilePath);
base64AndFileBean.setBase64StrList(Base64AndFileManager.getBase64StrList(txtFilePath));
// base64Str = "";
// StringBuilder stringBuilder = new StringBuilder();
// for (int i = 0; i < base64StrList.size(); i++) {
// stringBuilder.append(base64StrList.get(i));
// }
// base64Str = stringBuilder.toString();
// //把字符串的最後一個打印出來,然後看看和RecyclerView顯示的最後一個字符串是否一致
// LogManager.i(TAG, "base64StrList******" + base64StrList.get(base64StrList.size() - 1));
}
以上是我总结的超清大尺寸图片压缩转Base64速度优化问题,重点在第3点,循環壓縮bitmap(分成4次压缩了),防止一次壓縮bitmap卡頓問題,如果图片太大,就分成更多次压缩。
如对此有疑问,请联系qq1164688204。
推荐Android开源项目
项目功能介绍:RxJava2和Retrofit2项目,添加自动管理token功能,添加RxJava2生命周期管理,使用App架构设计是MVP模式和MVVM模式,同时使用组件化,部分代码使用Kotlin,此项目持续维护中。
项目地址:https://gitee.com/urasaki/RxJava2AndRetrofit2