美文网首页Android进阶之路Android开发Android开发经验谈
AndroidQ(10)分区存储完美适配之下载图片(文件)本地

AndroidQ(10)分区存储完美适配之下载图片(文件)本地

作者: 会撒娇的犀犀利 | 来源:发表于2019-12-18 14:11 被阅读0次

    前言

    在上一篇中我们已经讲述了AndroidQ(10)分区存储适配,包括如何在私有目录创建文件及文件夹公共目录下创建文件夹及文件公有目录下读取文件删除文件等重要功能。

    本篇文章将以开发中的实际情况出发,讲述在AndroidQ中如何将图片下载到公共目录简单来说就是如何将文件图片复制到另一个文件夹下。

      触发场景:gilde已经将图片缓存在本地,获取图片所在的gilde缓存路径,将图片复制到自己指定的目录~
      网络下载图片同理复制图片,只是传入不同类型的输入输出流而已~
    
    相关系列文章

    实际操作上手

    必要参数:原文件地址、需要复制的本地uri
    • 根据系统版本获取不同的文件路径

      1. AndroidQ以下,可以使用File API操作
      /**
      * AndroidQ以下
      * 创建图片缓存路径
      *
      * @param fileName 名称 包含文件类型
      * @return 返回file类型
      */
      public static File getImageFileCache (String fileName) {
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
              return null;
          }
          //创建项目图片公共缓存目录
          File file = new File(Environment.getExternalStorageDirectory()+
                          File.separator +
                          Environment.DIRECTORY_PICTURES +
                          File.separator +
                          "testAndroidQ" +
                          File.separator +
                          "images";);
          if (! file.exists()) {
              file.mkdirs();
          }
          //创建对应图片的缓存路径
          return new File(file + File.separator + fileName);
      }
      
      1. AndroidQ以上, 使用MediaStore生成对应文件图片缓存路径。不支持File API直接访问本地文件,如果使用会报无权限访问异常。
       /**
       * AndroidQ以上保存图片到公共目录
       *
       * @param imageName 图片名称
       * @param imageType 图片类型
       * @param relativePath 缓存路径
       */
      private static Uri insertImageFileIntoMediaStore (String imageName, String imageType,
          String relativePath) {
          if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
              return null;
          }
          if (TextUtils.isEmpty(relativePath)) {
              return null;
          }
          Uri insertUri = null;
          ContentResolver resolver = context.getContentResolver();
          //设置文件参数到ContentValues中
          ContentValues values = new ContentValues();
          //设置文件名
          values.put(MediaStore.Images.Media.DISPLAY_NAME, imageName);
          //设置文件描述,这里以文件名代替
          values.put(MediaStore.Images.Media.DESCRIPTION, imageName);
          //设置文件类型为image/*
          values.put(MediaStore.Images.Media.MIME_TYPE, "image/" + imageType);
          //注意:MediaStore.Images.Media.RELATIVE_PATH需要targetSdkVersion=29,
          //故该方法只可在Android10的手机上执行
          values.put(MediaStore.Images.Media.RELATIVE_PATH, relativePath);
          //EXTERNAL_CONTENT_URI代表外部存储器
          Uri external = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
          //insertUri表示文件保存的uri路径
          insertUri = resolver.insert(external, values);
          return insertUri;
      }
      
    • 通过两个路径地址生成对应的输入输出流

      主要方式获取ContentResolver.openOutputStream(insertUri)

    public static boolean copyFile (String sourceFilePath, final Uri insertUri) {
          if (insertUri == null) {
              return false;
          }
          ContentResolver resolver = context.getContentResolver();
          InputStream is = null;//输入流
          OutputStream os = null;//输出流
          try {
              os = resolver.openOutputStream(insertUri);
              if (os == null) {
                  return false;
              }
              File sourceFile = new File(sourceFilePath);
              if (sourceFile.exists()) { // 文件存在时
                  is = new FileInputStream(sourceFile); // 读入原文件
                  //输入流读取文件,输出流写入指定目录
                  return copyFileWithStream(os, is);
              }
              return false;
          } catch (Exception e) {
              e.printStackTrace();
              return false;
          } finally {
              try {
                  if (is != null) {
                      is.close();
                  }
                  if (os != null) {
                      os.close();
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
    }
    
    • 利用输入输出流边读边写,将文件(图片)写入指定路径
    private static boolean copyFileWithStream (OutputStream os, InputStream is) {
          if (os == null || is == null) {
              return false;
          }
          int read = 0;
          while (true) {
              try {
                  byte[] buffer = new byte[1444];
                  while ((read = is.read(buffer)) != - 1){
                      os.write(buffer, 0, read);
                      os.flush();
                  }
                  return true;
              } catch (IOException e) {
                  e.printStackTrace();
                  return false;
              } finally {
                  try {
                      os.close();
                      is.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
    }
    

    AndroidQ以下获取文件输入输出流就不贴代码了,注意区分就好啦~

    加载本地图片

    我们项目中使用的是Glide加载,故示例代码中也使用Glide

    GlideApp.with(context).load(imageUri);
    
    如果AndroidQ之前使用file://类型加载,兼容10可以采用file转uri的方式,都可以通过uri加载图片。

    图片上传

    AndroidQ分区存储适配图片上传

    注意:

    1. 在AndroidQ中公有目录访问文件,File API 都无法访问,即:file://本地path操作文件,本地加载图片、上传、下载都不支持,只能通过uri来操作

    2. 如果下载图片到公共目录,无需再发送广播通知图片更新;

    3. MediaStore.Images.Media.RELATIVE_PATH需要 targetSdkVersion=29 ,故该方法只可在Android10的手机上执行,如果在小于29的系统下调用RELATIVE_PATH会报错:

    android.database.sqlite.SQLiteException: table files has no column named relative_path (code 1 SQLITE_ERROR)
    
    数据库中插入无法找到relative_path字段
    本篇代码适用于文件读写、复制、保存图片到本地等等功能,灵活掌握ContentResolver

    后续会继续对AndroidQ分区存储实际操作做总结,欢迎关注~

    大佬点个赞再走呗~

    相关文章

      网友评论

        本文标题:AndroidQ(10)分区存储完美适配之下载图片(文件)本地

        本文链接:https://www.haomeiwen.com/subject/faqunctx.html