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

标签 android xamarin xamarin.android android-contentprovider filesavepicker

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

// 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);

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

content://com.android.providers.downloads.documents/document/436

但现在我悬而未决,因为文档的那部分以

结尾

After you create a new document you can get its URI in onActivityResult() so that you can continue to write to it.

我不知道该怎么做。由于我的结果使用的是 content 方案,所以我不能像对待常规 Java.IO.File 一样对待它并将其写入磁盘。那么如何将文件保存到我拥有的内容 uri?

最佳答案

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

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

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);
}

更新(性能):

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

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

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);
            }
        }
    });
}

关于android - 使用内容方案时保存文件的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51012702/

相关文章:

visual-studio - Xamarin 应用程序调试时出现空白屏幕

android - MvvmCross 是否依赖于 Google Play 服务?

android - 在异步方法中使用 Tesseract 代码

Android:在另一个 Activity 中重用存储为局部变量的 Activity

android - 无法运行我的信标应用程序,因为 kontakt sdk 错误

Android:Activity.onPause() 之后的点击事件

c# - Xamarin Studio 4.1.0.45在Mac OS X 10.6.8上启动时崩溃

c# - Xamarin Android - GetApplication

Android SQLite 列和索引最佳实践

android - 我怎样才能得到最后插入的事件的ID