Как правильно сохранить файл при использовании контента? - PullRequest
0 голосов
/ 24 июня 2018

Я использую следующее намерение, чтобы позволить пользователю выбрать, где файл должен быть сохранен:

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

Но теперь я оставлен в покое, потому что этот раздел документации заканчивается на

После создания нового документа вы можете получить его URI в onActivityResult (), чтобы продолжить запись в него.

И я не знаю, как это сделать. Поскольку мой результат заключается в использовании схемы content, я не могу просто обработать ее как обычный Java.IO.File и записать ее на диск. Так как же сохранить файл в имеющееся у меня содержимое?

1 Ответ

0 голосов
/ 24 июня 2018

Когда вы возвращаете содержимое 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);
}

Обновление (производительность):

Просто к сведению, если вы сохраняете / потоковые большие файлы, избегайте асинхронных версий чтения и записи в потоках и просто раскрутите один поток (или используйте один из пула потоков через 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);
            }
        }
    });
}
...