• Android保存多张图片到本地


    目录介绍

    • 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

  • 相关阅读:
    对scrapy经典框架爬虫原理的理解
    js的处理技巧
    网站登陆的两种方法
    scrapy批量下载图片
    [转]解决scrapy下载图片时相对路径转绝对路径的问题
    scrapy爬取西刺网站ip
    logging的使用方法
    scrapy中的response
    scrapy中的request
    scrapy.Spider的属性和方法
  • 原文地址:https://www.cnblogs.com/yc211/p/11015795.html
Copyright © 2020-2023  润新知