目录介绍

  • 01.实际开发保存图片遇到的问题
  • 02.直接用http请求图片并保存本地
  • 03.用glide下载图片保存本地
  • 04.如何实现连续保存多张图片
  • 05.关于其他介绍

好消息

  • 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计N篇[近100万字,陆续搬到网上],转载请注明出处,谢谢!
  • 链接地址:https://github.com/yangchong211/YCBlogs
  • 如果觉得好,可以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的大小来限制图片的大小。例如如果要加载的图片是1000x1000像素,但是ImageView的尺寸只有250x250像素,Glide会降低图片到小尺寸,以节省处理时间和内存。
    • 在设置into控件后,也就是说,在onResourceReady方法中返回的图片资源resource,实质上不是你加载的原图片,而是ImageView设定尺寸大小的图片。所以保存之后,你会发现图片变小了。
  • 那么如何解决问题呢?
    • 第一种做法:九宫格图片控件展示的时候会加载网络资源,然后加载图片成功后,则将资源保存到集合中,点击保存则循环存储集合中的资源。这种做法只会请求一个网络。由于开始
    • 第二种做法:九宫格图片控件展示的时候会加载网络资源,点击保存九宫格图片的时候,则依次循环请求网络图片资源然后保存图片到本地,这种做法会请求两次网络。

02.直接用http请求图片并保存本地

  • http请求图片

    /**
    * 请求网络图片
    * @param url url
    * @return 将url图片转化成bitmap对象
    */
    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.关于博客汇总链接

02.关于我的博客

项目案例:https://github.com/yangchong211/YCVideoPlayer

Android保存多张图片到本地的更多相关文章

  1. Android开发之下载服务器上的一张图片到本地java代码实现HttpURLConnection

    package com.david.HttpURLConnectionDemo; import java.io.FileOutputStream; import java.io.IOException ...

  2. 通过wget工具下载指定文件中的URLs对应的资源并保存到指定的本地目录中去并进行文件完整性与可靠性校验

    创建URLs文件在终端输入cd target_directory回车,便把当前文件夹切换到了目标文件夹target_directory,此后创建的文件都会丢它里面在终端输入cat > URLs回 ...

  3. Android单元测试之二:本地测试

    Android单元测试之二:本地测试 本地测试 本地测试( Local tests):只在本地机器 JVM 上运行,以最小化执行时间,这种单元测试不依赖于 Android 框架,或者即使有依赖,也很方 ...

  4. telnet如何保存输出内容到本地

    telnet如何保存输出内容到本地 http://bbs.csdn.net/topics/391023327 一种将程序的标准输出重定向到telnet终端的方法 http://blog.chinaun ...

  5. C#读取资源文件的两种方法及保存资源文件到本地

    方法1 GetManifestResourceStream   VB.NET中资源的名称为:项目默认命名空间.资源文件名 C#中则是:项目命名空间.资源文件所在文件夹名.资源文件名 例如:istr = ...

  6. [开源类库/项目] android保存崩溃时的错误信息log至本地【源码+jar包+使用说...

    不知大家是否经常遇到这种情况:自己的项目有时会在没有连接到电脑时发生崩溃,好不容易发现的bug结果连接到电脑时又复现不出来了:又或者自己写的一个功能在开机启动时产生小bug导致崩溃,而刚启动的机器想让 ...

  7. android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地

    http://blog.csdn.net/jason0539/article/details/45602655 应用发生crash之后要查看log,判断问题出在什么地方,可是一旦应用发布出去,就要想办 ...

  8. android 保存Bitmap 到本地 哦

    public String saveImage(Bitmap bmp) { File appDir = new File(Environment.getExternalStorageDirectory ...

  9. android 保存 用户名和密码 设置等应用信息优化

    1.传统的保存用户名,密码方式 SharedPreferences Editor editor = shareReference.edit(); editor.putString(KEY_NAME,& ...

  10. Android 保存联系人,包括部门\职位\传真\地址\照片

    private void toSaveContactInfo() { ContentValues values = new ContentValues(); // 首先向RawContacts.CON ...

随机推荐

  1. .NET Core开发实战(第9课:命令行配置提供程序)--学习笔记

    09 | 命令行配置提供程序:最简单快捷的配置注入方法 这一节讲解如何使用命令行参数来作为配置数据源 命令行配置(提供程序的)支持三种格式的命令 1.无前缀的 key=value 模式 2.双中横线模 ...

  2. Java-统计程序运行的时长(计算两个时间相差的毫秒数、秒数)

    最近在做Hbase的查询性能验证,需要统计查询的执行时长,所以需要统计开始时间和结束时间的时间差. 下面是使用SimpleDateFormat和Date计算时间差(相差毫秒数)的程序示例,仅供参考. ...

  3. P2898 [USACO08JAN] Haybale Guessing G 题解

    题目传送门 前置知识 二分答案 | 并查集 解法 对条件的合法性判断其他题解已经讲得很明白了,这里不再赘述.这里主要讲一下用并查集实现黑白染色问题. 以下内容称被覆盖为黑色,不被覆盖为白色. 本题因为 ...

  4. CF1829H Don't Blame Me

    题目链接 题解 知识点:线性dp,位运算. 考虑设 \(f_{i,j}\) 表示考虑了前 \(i\) 个数字,与和为 \(j\) 的方案数.转移方程显然. 注意初值为 \(f_{0,63} = 1\) ...

  5. NC16666 [NOIP2006]开心的金明

    题目链接 题目 题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:"你的房间需要购买哪些物品,怎么布置,你说了算,只 ...

  6. 【Unity3D】地形Terrain

    1 前言 ​ Terrain 是 Unity3D 提供的用于绘制地形的游戏对象,可以在其上绘制山地.江海.池塘.草树等. ​ 用户可以通过[GameObject → 3D Object → Terra ...

  7. useEffect与useLayoutEffect

    useEffect与useLayoutEffect useEffect与useLayoutEffect可以统称为Effect Hook,Effect Hook可以在函数组件中执行副作用操作,副作用是指 ...

  8. java.lang.System快速指南

    1.介绍 在本教程中,我们将快速了解java.lang.System类及其特性和核心功能. 2.IO 系统类是java.lang的一部分,它的一个主要特性是让我们能够访问标准的I/O流. 简单地说,它 ...

  9. 【Android 逆向】【ARM汇编】 事前更新和事后更新

    1. 事前更新,事后更新,不更新 不更新 ldr R4, [R1, R2, lsl #1] 相当于 R4 = *(R1 + R2 << 2^1) 之后 R1.R2的值时没有变化的 事前更新 ...

  10. Java 使用 itext 向PDF插入数据和图片

    Java 使用 itext 向PDF插入数据和图片 一.下载Adobe Acrobat DC 二.制作模板 1.准备一个word模板,并转换成PDF格式 2.使用Adobe Acrobat DC打开P ...