使用内容方案时保存文件的正确方法是什么?

use*_*234 5 android xamarin.android android-contentprovider xamarin filesavepicker

我使用以下意图来允许用户选择文件的保存位置:

// https://developer.android.com/guide/topics/providers/document-provider
var intent = new Intent(Intent.ActionCreateDocument);
intent.AddCategory(Intent.CategoryOpenable);
intent.SetType("image/png");
intent.PutExtra(Intent.ExtraTitle, "myfile");
StartActivityForResult(Intent.CreateChooser(intent, "Select Save Location"), 43);
Run Code Online (Sandbox Code Playgroud)

它创建文件并返回文件的 uri:

content://com.android.providers.downloads.documents/document/436
Run Code Online (Sandbox Code Playgroud)

但是现在我被搁置了,因为文档的那部分以

创建新文档后,您可以在 onActivityResult() 中获取其 URI,以便您可以继续写入。

我不知道该怎么做。由于我的结果是使用该content方案,因此我不能将其视为常规Java.IO.File并将其写入磁盘。那么如何将文件保存到我拥有的内容 uri 中呢?

Sus*_*ver 3

当您返回内容 Uri 时,OnActivityResult您对该 Uri 具有临时权限(因此具有写入权限),因此您可以在写入模式下打开基于包的文件描述符,从该包的文件描述符创建输出流并写入任何内容你需要它。

将资产流写入用户选择的文件的示例:

protected async override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
    if (resultCode == Result.Ok && requestCode == 43)
    {
        var buffer = new byte[1024];
        using (var pFD = ContentResolver.OpenFileDescriptor(data.Data, "w"))
        using (var outputSteam = new FileOutputStream(pFD.FileDescriptor))
        using (var inputStream = Assets.Open("somePicture.png"))
        {
            while (inputStream.CanRead && inputStream.IsDataAvailable())
            {
                var readCount = await inputStream.ReadAsync(buffer, 0, buffer.Length);
                await outputSteam.WriteAsync(buffer, 0, readCount);
            }
        }

    }
    base.OnActivityResult(requestCode, resultCode, data);
}
Run Code Online (Sandbox Code Playgroud)

更新(性能):

仅供参考,如果您要保存/流式传输大文件,请避免在流上使用异步版本的读取和写入,而只需启动单个线程(或通过 Task.Run 使用线程池中的一个线程)。

注意:由于所有的异步/等待开销,这总是会更快,并且有点像我通常的做法(通常根据文件大小快 2 倍(+))。

if (resultCode == Result.Ok && requestCode == 43)
{
    await Task.Run(() =>
    {
        // Buffer size can be "tuned" to enhance read/write performance
        var buffer = new byte[1024]; 
        using (var pFD = ContentResolver.OpenFileDescriptor(data.Data, "w"))
        using (var outputSteam = new FileOutputStream(pFD.FileDescriptor))
        using (var inputStream = Assets.Open("her.png"))
        {
            while (inputStream.CanRead && inputStream.IsDataAvailable())
            {
                var readCount = inputStream.Read(buffer, 0, buffer.Length);
                outputSteam.Write(buffer, 0, readCount);
            }
        }
    });
}
Run Code Online (Sandbox Code Playgroud)