【问题标题】:How to save image to camera folder in Android Q?如何将图像保存到 Android Q 中的相机文件夹?
【发布时间】:2019-12-12 21:26:40
【问题描述】:

我需要将图像保存到相机文件夹,但由于不推荐使用 Android Q getExternalStoragePublicDirectory,我以另一种方式进行操作。 我有什么(这个方法接收位图和它的名字):

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        ContentResolver resolver = mContext.getContentResolver();
        ContentValues contentValues = new ContentValues();
        contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
        contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
        contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/" + IMAGES_FOLDER_NAME);
        Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
        OutputStream fos = resolver.openOutputStream(imageUri);
        saved = bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
        fos.flush();
        fos.close();
    } else {
        String imagesDir = Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_DCIM).toString() + File.separator + IMAGES_FOLDER_NAME;

        File file = new File(imagesDir);

        if (!file.exists()) {
            file.mkdir();
        }

        File image = new File(
                imagesDir,
                name + ".png"
        );

        final long fileHashCode = image.hashCode();
        Logger.d(TAG, "saveImage, saving image file, hashCode = " + fileHashCode);

        FileOutputStream fos = new FileOutputStream(image);
        saved = bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
        fos.flush();
        fos.close();
    }

这完全适用于所有需要的操作系统版本,但它看起来不准确,我想找到一种更通用的方法。 我尝试使用内容值或尝试与 Q 类似的方式,但它不起作用。我在这里看到了很多问题,但它们都不能帮助我。

问题是如何优化低于 Q 的操作系统的节省?

【问题讨论】:

  • “但它看起来不准确”——对我来说似乎没问题。在 Q 上,您可以在写出内容时使用IS_PENDING。 “如何优化低于 Q 的操作系统的节省?” -- 除了fileHashCode 这对线之外,你所拥有的似乎是合理的。
  • @CommonsWare,没关系我用不同的方式保存它?没有办法像 Q 那样保存图像?我正在寻找另一个用于插入的 URI,但什么也没有。
  • "所以我可以用不同的方式保存它吗?" - 当然。 “有没有办法像Q一样保存图像?” -- 不在特定位置。 RELATIVE_PATH 在 Q 之前不存在。请注意,MediaStore 不会立即在代码的旧于 Q 分支中知道您的图像。如果这对您很重要,请使用MediaScannerConnection.scanFile()
  • @pic:使用Uri,因为没有可以使用的文件系统路径。 Glide、Picasso 和其他优秀的图像加载库可以显示由MediaStoreUri 标识的图像。或者,使用ContentResolveropenInputStream() 自己读取图像字节。
  • @pic:您需要查询MediaStore。如果您对此过程有任何疑问,并且找不到有关它的现有答案,请提出单独的 Stack Overflow 问题。

标签: android android-10.0


【解决方案1】:

我能写的最通用的版本是:

private Uri saveImage(Context context, Bitmap bitmap, @NonNull String folderName, @NonNull String fileName) throws IOException {
    OutputStream fos = null;
    File imageFile = null;
    Uri imageUri = null;

    try {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            ContentResolver resolver = context.getContentResolver();
            ContentValues contentValues = new ContentValues();
            contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
            contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
            contentValues.put(
                    MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator + folderName);
            imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);

            if (imageUri == null)
                throw new IOException("Failed to create new MediaStore record.");

            fos = resolver.openOutputStream(imageUri);
        } else {
            File imagesDir = new File(Environment.getExternalStoragePublicDirectory(
                    Environment.DIRECTORY_PICTURES).toString() + File.separator + folderName);

            if (!imagesDir.exists())
                imagesDir.mkdir();

            imageFile = new File(imagesDir, fileName + ".png");
            fos = new FileOutputStream(imageFile);
        }


        if (!bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos))
            throw new IOException("Failed to save bitmap.");
        fos.flush();
    } finally {
        if (fos != null)
            fos.close();
    }
    
    if (imageFile != null) {//pre Q
        MediaScannerConnection.scanFile(context, new String[]{imageFile.toString()}, null, null);
        imageUri = Uri.fromFile(imageFile);
    }
    return imageUri;
}

如果您找到了更好的方法,请在此处发布,我会将其标记为答案。

【讨论】:

  • 如何使用 Environment.getExternalStorageDirectory() +"DCIM/" + IMAGES_FOLDER_NAME bcz 显示此图像,因为它在 android 10 中不起作用
  • IMAGES_FOLDER_NAME 是干什么用的?需要自定义文件夹名称吗?
  • @RRGT19,可选文件夹名称。
  • 我见过的最佳解决方案
【解决方案2】:

使用本文档:https://developer.android.com/training/data-storage

先创建临时文件

val mTempFileRandom = Random()

fun createTempFile(ext:String, context:Context):String {
  val path = File(context.getExternalCacheDir(), "AppFolderName")
  if (!path.exists() && !path.mkdirs())
  {
    path = context.getExternalCacheDir()
  }
  val result:File
  do
  {
    val value = Math.abs(mTempFileRandom.nextInt())
    result = File(path, "AppFolderName-" + value + "-" + ext)
  }
  while (result.exists())
  return result.getAbsolutePath()
}   

从路径发送文件

copyFileToDownloads(this@CameraNewActivity, File(savedUri.path))

复制数据到存储

MAIN_DIR:您要在其中存储图像的主文件夹名称(如应用名称) IMAGE_DIR:如果要创建子文件夹。

    fun copyFileToDownloads(context: Context, downloadedFile: File): Uri? {

    // Create an image file name
    val timeStamp = SimpleDateFormat(DATE_FORMAT_SAVE_IMAGE).format(Date())
    val imageFileName = "JPEG_$timeStamp.jpg"
    val resolver = context.contentResolver

    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val values = ContentValues().apply {
            put(MediaStore.Images.Media.DISPLAY_NAME, imageFileName)
            put(MediaStore.Images.Media.MIME_TYPE, IMAGE_MIME_TYPE)
            put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM + File.separator + MAIN_DIR + File.separator + IMAGE_DIR + File.separator)
        }

        resolver.run {
            val uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)

            uri
        }
    } else {
        val authority = "${context.packageName}.provider"
        val imagePath =
            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)?.absolutePath
        val destinyFile = File(imagePath, imageFileName)
        val uri = FileProvider.getUriForFile(context, authority, destinyFile)
        FileUtils.scanFile(context, destinyFile.absolutePath)

        uri
    }?.also { uri ->
        var writtenValue = 0L
        // Opening an outputstream with the Uri that we got
        resolver.openOutputStream(uri)?.use { outputStream ->
            downloadedFile.inputStream().use { inputStream ->
                writtenValue = inputStream.copyTo(outputStream)
                Log.d("Copy Written flag", " = $writtenValue")
            }
        }
    }
}

扫描文件:(更多细节:https://developer.android.com/reference/android/media/MediaScannerConnection

fun scanFile(context:Context, path:String) {
  MediaScannerConnection.scanFile(context,
                                  arrayOf<String>(path), null,
                                  { newPath, uri-> if (BuildConfig.DEBUG)
                                   Log.e("TAG", "Finished scanning " + newPath) })
}

【讨论】:

    【解决方案3】:

    我们不能在 Android Q 之上和之下使用媒体商店吗? 我尝试了以下方法,它工作正常。

    private fun writeImage() {
        val uri =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
            } else {
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI
            }
    
        val imageDetail = ContentValues().apply {
            put(MediaStore.Images.ImageColumns.DISPLAY_NAME, "${System.currentTimeMillis()}.jpeg")
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                put(MediaStore.Images.Media.IS_PENDING, 1)
            }
        }
        val contentUri = contentResolver.insert(uri, imageDetail)
    
        contentUri?.let {
            contentResolver.openFileDescriptor(contentUri, "w", null).use { pd ->
                pd?.let {
                    /* val fos = FileOutputStream(it.fileDescriptor)
                       val array = getBitmapToBase64()
                       fos.write(array, 0, array.size)
                       fos.close() 
                    */
                    // or
                    // Your logic to write an Image file.
                }
            }
    
            imageDetail.clear()
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                imageDetail.put(MediaStore.Images.Media.IS_PENDING, 0)
                contentUri.let { contentResolver.update(it, imageDetail, null, null) }
            }
    
            // open the saved image file with gallery app
            Snackbar.make(
                findViewById(android.R.id.content), "saved", Snackbar.LENGTH_LONG
            ).setAction("Show") {
                val intent = Intent(Intent.ACTION_VIEW, contentUri)
                startActivity(intent)
            }.show()
    
        } ?: Toast.makeText(this, "not saved", Toast.LENGTH_LONG).show()
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-08
      • 1970-01-01
      • 2012-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-04
      相关资源
      最近更新 更多