Android 从Storage Access Framework UI获取文件夹后保存图像

Android 从Storage Access Framework UI获取文件夹后保存图像,android,storage-access-framework,Android,Storage Access Framework,我为用户设置了一个首选项,以便使用Storage Access Framework为我的应用程序选择“保存文件夹”。在获得urionActivityResult后,我将其保存到SharedReferences作为字符串,并在保存图像时保存图像 我正在使用此方法成功保存图像 public void saveImageWithDocumentFile(String uriString, String mimeType, String name) { isImageSaved = false;

我为用户设置了一个首选项,以便使用Storage Access Framework为我的应用程序选择“保存文件夹”。在获得uri
onActivityResult
后,我将其保存到
SharedReferences
作为字符串,并在保存图像时保存图像

我正在使用此方法成功保存图像

public void saveImageWithDocumentFile(String uriString, String mimeType, String name) {
    isImageSaved = false;
    try {
        Uri uri = Uri.parse(uriString);
        DocumentFile pickedDir = DocumentFile.fromTreeUri(this, uri);
        DocumentFile file = pickedDir.createFile(mimeType, name);
        OutputStream out = getContentResolver().openOutputStream(file.getUri());
        isImageSaved = mBitmap.compress(CompressFormat.JPEG, 100, out);
        out.close();

    } catch (IOException e) {
        throw new RuntimeException("Something went wrong : " + e.getMessage(), e);
    }
    Toast.makeText(MainActivity.this, "Image saved  " + isImageSaved, Toast.LENGTH_SHORT).show();
}
如果用户删除使用SAF选择的文件夹,iget null
DocumentFile
和应用程序崩溃。我的第一个问题是如何在不再次打开SAF ui的情况下检查文件夹是否存在

我还想使用ParcelFileDescriptor使用此方法保存相同的图像

public void saveImageWithParcelFileDescriptor(String folder, String name) {
    if (mBitmap == null) {
        Toast.makeText(MainActivity.this, "", Toast.LENGTH_SHORT).show();
        return;
    }
    String image = folder + File.separator + name + ".jpg";
    Toast.makeText(MainActivity.this, "image " + image, Toast.LENGTH_LONG).show();

    Uri uri = Uri.parse(image);
    ParcelFileDescriptor pfd = null;
    FileOutputStream fileOutputStream = null;
    try {
        pfd = getContentResolver().openFileDescriptor(uri, "w");
        fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
        mBitmap.compress(CompressFormat.JPEG, 100, fileOutputStream);

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } finally {
        if (pfd != null) {
            try {
                pfd.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (fileOutputStream != null) {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    Toast.makeText(MainActivity.this, "Image saved  " + isImageSaved, Toast.LENGTH_SHORT).show();

}
我得到
java.lang.IllegalArgumentException:无效URI:content://com.android.externalstorage.documents/tree/612E-B7BF%3Aname/imagePFD.jpg
在线
pfd=getContentResolver().openFileDescriptor(uri,“w”)

这就是我如何调用这个方法,currentFolder是我使用intent和第一个方法上的相同文件夹得到的文件夹

saveImageWithParcelFileDescriptor(currentFolder, "imagePFD");
我如何解决这个问题,哪种方法更可取,为什么

如何检查文件夹是否存在

DocumentFile
有一个
exists()
方法。理论上,
pickedDir.exists()
应该告诉您它是否存在。实际上,这取决于存储提供商

我还想使用ParcelFileDescriptor使用此方法保存相同的图像

public void saveImageWithParcelFileDescriptor(String folder, String name) {
    if (mBitmap == null) {
        Toast.makeText(MainActivity.this, "", Toast.LENGTH_SHORT).show();
        return;
    }
    String image = folder + File.separator + name + ".jpg";
    Toast.makeText(MainActivity.this, "image " + image, Toast.LENGTH_LONG).show();

    Uri uri = Uri.parse(image);
    ParcelFileDescriptor pfd = null;
    FileOutputStream fileOutputStream = null;
    try {
        pfd = getContentResolver().openFileDescriptor(uri, "w");
        fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
        mBitmap.compress(CompressFormat.JPEG, 100, fileOutputStream);

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } finally {
        if (pfd != null) {
            try {
                pfd.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (fileOutputStream != null) {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    Toast.makeText(MainActivity.this, "Image saved  " + isImageSaved, Toast.LENGTH_SHORT).show();

}
该代码有错误:

  • 您不能使用
    grantUriPermission()

  • 您不能通过连接创建任意的
    Uri
    值,特别是对于不是您的提供者

我怎样才能解决这个问题

删除它

哪种方法更可取

第一个

为什么


第一种方法会起作用,也许需要稍作调整。第二个不可能工作。

是的,只要保存的文件夹存在,第一个就可以工作。我将使用documentFile.exist()检查文件夹是否存在。难道没有办法用自定义名称和ParcelFileDescriptor保存图像吗?@FatihOzcan:欢迎您将第一个代码段中的
getContentResolver().openOutputStream out=getContentResolver().openOutputStream(file.getUri())
替换为
getContentResolver().openFileDescriptor(file.getUri(),“w”)
然后用一个
ParcelFileDescriptor
那样做。我不知道这样做的好处是什么,结果会比第一个代码段长很多,而且结果与第二个代码段不太相似。密钥使用的是
createFile()
,因此存储提供程序就是您写入的
Uri
的提供者。我删除了
grantUriPermission()
。获取权限或持久权限仅对您在ActivityResult上获取的uri有效?文档中没有关于此的信息。关于文档“您的应用程序访问的最新URI可能不再有效。另一个应用程序可能已删除或修改了文档。因此,您应该始终调用getContentResolver().takePersistableUriPermission()检查最新的数据。”但如何使用此方法检查文件夹是否存在。它没有返回任何内容。我还有另一个问题。难道没有办法用ParceFileDescriptor编写自定义文件吗?如果不使用OutputStream,则无法将位图保存到设备或sd卡。ParcelFileDescriptor是否用于写入自定义文件?我不知道如何在第二天用ParcelFileDescriptor交换OutputStremmethod@FatihOzcan:“获取权限或持久权限仅对您在ActivityResult上获取的uri有效?”--您可以调用
takePersistableUriPermission()
on
Uri
存储访问框架操作向
onActivityResult()
传递的值。您应该能够将它与
DocumentFile
返回的
Uri
值一起使用(例如,
createFile()
),尽管我没有尝试过。“如何使用此方法检查文件夹是否存在”--它将抛出一个
SecurityException
,IIRC。