01.实际开发保存图片遇到的问题
业务需求
在素材list页面的九宫格素材中,展示网络请求加载的图片。如果用户点击保存按钮,则保存若干张图片到本地。具体做法是,使用glide加载图片,然后设置listener监听,在图片请求成功onResourceReady后,将图片资源resource保存到集合中。这个时候,如果点击保存控件,则循环遍历图片资源集合保存到本地文件夹。
具体做法代码展示
这个时候直接将请求网络的图片转化成bitmap,然后存储到集合中。然后当点击保存按钮的时候,将会保存该组集合中的多张图片到本地文件夹中。
//bitmap图片集合
private ArrayList<Bitmap> bitmapArrayList = new ArrayList<>();
RequestOptions requestOptions = new RequestOptions()
.transform(new GlideRoundTransform(mContext, radius, cornerType));
GlideApp.with(mIvImg.getContext())
.asBitmap()
.load(url)
.listener(new RequestListener<Bitmap>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model,
Target<Bitmap> target, boolean isFirstResource) {
return true;
}
@Override
public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target,
DataSource dataSource, boolean isFirstResource) {
bitmapArrayList.add(resource);
return false;
}
})
.apply(requestOptions)
.placeholder(ImageUtils.getDefaultImage())
.into(mIvImg);
//循环遍历图片资源集合,然后开始保存图片到本地文件夹
mBitmap = bitmapArrayList.get(i);
savePath = FileSaveUtils.getLocalImgSavePath();
FileOutputStream fos = null;
try {
File filePic = new File(savePath);
if (!filePic.exists()) {
filePic.getParentFile().mkdirs();
filePic.createNewFile();
}
fos = new FileOutputStream(filePic);
// 100 图片品质为满
mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
if (fos != null) {
try {
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//刷新相册
if (isScanner) {
scanner(context, savePath);
}
}
遇到的问题
保存图片到本地后,发现图片并不是原始的图片,而是展现在view控件上被裁切的图片,也就是ImageView的尺寸大小图片。
为什么会遇到这种问题
如果你传递一个ImageView作为.into()的参数,Glide会使用ImageView的大小来限制图片的大小。例如如果要加载的图片是1000x1000像素,但是ImageView的尺寸只有250x250像素,Glide会降低图片到小尺寸,以节省处理时间和内存。
在设置into控件后,也就是说,在onResourceReady方法中返回的图片资源resource,实质上不是你加载的原图片,而是ImageView设定尺寸大小的图片。所以保存之后,你会发现图片变小了。
那么如何解决问题呢?
第一种做法:九宫格图片控件展示的时候会加载网络资源,然后加载图片成功后,则将资源保存到集合中,点击保存则循环存储集合中的资源。这种做法只会请求一个网络。由于开始
第二种做法:九宫格图片控件展示的时候会加载网络资源,点击保存九宫格图片的时候,则依次循环请求网络图片资源然后保存图片到本地,这种做法会请求两次网络。
02.直接用http请求图片并保存本地
http请求图片
/**
* 请求网络图片
* @param url url
*/
private static long time = 0;
public static InputStream HttpImage(String url) {
long l1 = System.currentTimeMillis();
URL myFileUrl = null;
Bitmap bitmap = null;
HttpURLConnection conn = null;
InputStream is = null;
try {
myFileUrl = new URL(url);
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
conn = (HttpURLConnection) myFileUrl.openConnection();
conn.setConnectTimeout(10000);
conn.setReadTimeout(5000);
conn.setDoInput(true);
conn.connect();
is = conn.getInputStream();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
conn.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
long l2 = System.currentTimeMillis();
time = (l2-l1) + time;
LogUtils.e("毫秒值"+time);
//保存
}
return is;
}
```
保存到本地
InputStream inputStream = HttpImage(
"https://img1.haowmc.com/hwmc/material/2019061079934131.jpg");
String localImgSavePath = FileSaveUtils.getLocalImgSavePath();
File imageFile = new File(localImgSavePath);
if (!imageFile.exists()) {
imageFile.getParentFile().mkdirs();
try {
imageFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
FileOutputStream fos = null;
BufferedInputStream bis = null;
try {
fos = new FileOutputStream(imageFile);
bis = new BufferedInputStream(inputStream);
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (bis != null) {
bis.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
03.用glide下载图片保存本地
glide下载图片
File file = Glide.with(ReflexActivity.this)
.load(url.get(0))
.downloadOnly(500, 500)
.get();
保存到本地
String localImgSavePath = FileSaveUtils.getLocalImgSavePath();
File imageFile = new File(localImgSavePath);
if (!imageFile.exists()) {
imageFile.getParentFile().mkdirs();
imageFile.createNewFile();
}
copy(file,imageFile);
/**
*
* @param source 输入文件
* @param target 输出文件
*/
public static void copy(File source, File target) {
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
fileInputStream = new FileInputStream(source);
fileOutputStream = new FileOutputStream(target);
byte[] buffer = new byte[1024];
while (fileInputStream.read(buffer) > 0) {
fileOutputStream.write(buffer);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fileInputStream != null) {
fileInputStream.close();
}
if (fileOutputStream != null) {
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
04.如何实现连续保存多张图片
思路:循环子线程
- 可行(不推荐), 如果我要下载9个图片,将子线程加入for循环内,并最终呈现。
- 有严重缺陷,线程延时,图片顺序不能做保证。如果是线程套线程的话,第一个子线程结束了,嵌套在该子线程f的or循环内的子线程还没结束,从而主线程获取不到子线程里获取的图片。
- 还有就是如何判断所有线程执行完毕,比如所有图片下载完成后,吐司下载完成。
不建议的方案
创建一个线程池来管理线程,关于线程池封装库,可以看线程池简单封装
这种方案不知道所有线程中请求图片是否全部完成,且不能保证顺序。
ArrayList<String> images = new ArrayList<>();
for (String image : images){
//使用该线程池,及时run方法中执行异常也不会崩溃
PoolThread executor = BaseApplication.getApplication().getExecutor();
executor.setName("getImage");
executor.execute(new Runnable() {
@Override
public void run() {
//请求网络图片并保存到本地操作
}
});
}
推荐解决方案
ArrayList<String> images = new ArrayList<>();
ApiService apiService = RetrofitService.getInstance().getApiService();
//注意:此处是保存多张图片,可以采用异步线程
ArrayList<Observable<Boolean>> observables = new ArrayList<>();
final AtomicInteger count = new AtomicInteger();
for (String image : images){
observables.add(apiService.downloadImage(image)
.subscribeOn(Schedulers.io())
.map(new Function<ResponseBody, Boolean>() {
@Override
public Boolean apply(ResponseBody responseBody) throws Exception {
saveIo(responseBody.byteStream());
return true;
}
}));
}
// observable的merge 将所有的observable合成一个Observable,所有的observable同时发射数据
Disposable subscribe = Observable.merge(observables).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Boolean>() {
@Override
public void accept(Boolean b) throws Exception {
if (b) {
count.addAndGet(1);
Log.e("yc", "download is succcess");
}
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Log.e("yc", "download error");
}
}, new Action() {
@Override
public void run() throws Exception {
Log.e("yc", "download complete");
// 下载成功的数量 和 图片集合的数量一致,说明全部下载成功了
if (images.size() == count.get()) {
ToastUtils.showRoundRectToast("保存成功");
} else {
if (count.get() == 0) {
ToastUtils.showRoundRectToast("保存失败");
} else {
ToastUtils.showRoundRectToast("因网络问题 保存成功" + count + ",保存失败" + (images.size() - count.get()));
}
}
}
}, new Consumer<Disposable>() {
@Override
public void accept(Disposable disposable) throws Exception {
Log.e("yc","disposable");
}
});
private void saveIo(InputStream inputStream){
String localImgSavePath = FileSaveUtils.getLocalImgSavePath();
File imageFile = new File(localImgSavePath);
if (!imageFile.exists()) {
imageFile.getParentFile().mkdirs();
try {
imageFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
FileOutputStream fos = null;
BufferedInputStream bis = null;
try {
fos = new FileOutputStream(imageFile);
bis = new BufferedInputStream(inputStream);
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (bis != null) {
bis.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
//刷新相册代码省略……
}
}
链接地址:https://github.com/yangchong2...
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对得得之家的支持。