目录介绍
- 01. 实际开发保存图片遇到的问题
- 02. 直接用 http 请求图片并保存本地
- 03. 用 glide 下载图片保存本地
- 04. 如何实现连续保存多张图片
- 05. 关于其他介绍
好消息
- 博客笔记大汇总【16 年 3 月到至今】,包括 Java 基础及深入知识点,Android 技术博客,Python 学习笔记等等,还包括平时开发中遇到的 bug 汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是 markdown 格式的!同时也开源了生活博客,从 12 年起,积累共计 N 篇 [近 100 万字,陆续搬到网上],转载请注明出处,谢谢!
- 链接地址:https://github.com/yangchong2…
- 如果觉得好,可以 star 一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!
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 的大小来限制图片的大小。例如如果要加载的图片是 1000×1000 像素,但是 ImageView 的尺寸只有 250×250 像素,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(); } // 刷新相册代码省略…… } }
其他介绍
01. 关于博客汇总链接
- 1. 技术博客汇总
- 2. 开源项目汇总
- 3. 生活博客汇总
- 4. 喜马拉雅音频汇总
- 5. 其他汇总
02. 关于我的博客
- github:https://github.com/yangchong211
- 知乎:https://www.zhihu.com/people/…
- 简书:http://www.jianshu.com/u/b7b2…
- csdn:http://my.csdn.net/m0_37700275
- 喜马拉雅听书:http://www.ximalaya.com/zhubo…
- 开源中国:https://my.oschina.net/zbj161…
- 泡在网上的日子:http://www.jcodecraeer.com/me…
- 邮箱:yangchong211@163.com
- 阿里云博客:https://yq.aliyun.com/users/a… 239.headeruserinfo.3.dT4bcV
- segmentfault 头条:https://segmentfault.com/u/xi…
- 掘金:https://juejin.im/user/593943…