Как правильно перезаписать содержимое файла с помощью Android Access Framework доступа - PullRequest
0 голосов
/ 05 июля 2019

>> Фон

Я хочу использовать SAF (фреймворк доступа к хранилищу) для сохранения файлов данных моего приложения в нужном месте пользователя на носителе.Сначала я создаю файл в специальной папке приложения, а затем копирую его в файл, выбранный пользователями в диалоговом окне выбора файлов (коды поступают позже).

эта процедура отлично работает для новых файлов но для существующих файлов, хотя средство выбора файлов предупреждает о перезаписи файла, окончательный файл не стирается перед записью.

Подсчитывая записанные байты и исследуя файлы с помощью шестнадцатеричного редактора, код записывает правильные байты ввыходной поток, но: если в существующем файле записано больше байтов, чем байтов, окончательно перезаписанный файл поврежден (не поврежден, см. следующие разделы для уточнения), и если существующий файл имеет меньше байтов, чем байтовзаписано, окончательный перезаписанный файл является правильным.

>> Подробнее и коды

Я использую коды ниже, чтобы показать проблему (JPG какпример): и я попытаюсь работать с двумя файлами:

file1.jpg 166,907 bytes
file2.jpg 1,323,647 bytes
file3.jpg The final file with variable size

Сначала я скопирую файл1 для пользователявыберите папку с именем file3 (конечный файл), затем перезапишите ее с file2 и, наконец, я перезапишу ее снова с file1.посмотрите, что за коды и что происходит:

код для вызова средства выбора файлов:

val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
    addCategory(Intent.CATEGORY_OPENABLE)
    type = "image/jpeg"
}
startActivityForResult(intent, request)

Теперь в onActivityResult () я обрабатываю данные следующим образом:

contentResolver.openOutputStream(fileUri)?.use {output->
        val input = FileInputStream(File(getExternalFilesDir(null),"Pictures/file1.jpg"))
    // file1.jpg for first run, file2.jpg for 2nd run and file1.jpg again for 3rd run     
        copyStream(input, output)

    }

И код для копирования потока:

@Throws(IOException::class)
fun copyStream(input: InputStream, output: OutputStream) {
    val buffer = ByteArray(1024)
    var bytesRead = input.read(buffer)
    while (bytesRead > 0) {
        output.write(buffer, 0, bytesRead)
        bytesRead = input.read(buffer)
    }
    input.close()
    //The output will be closes by kotlin standard function "use" at previous code
}

Теперь сначала запустите file3.jpg точно так же, как file1.jpg.также файл3.jpg такой же, как файл2.jpg при втором запуске.но при третьем запуске, который перезаписывает файл file3.jpg содержимым файла file1.jpg (который имеет kess-байтов больше, чем file3.jpg), размер файла file3.jpg остается 1,323,647 байт, а первые 166,907 байт совпадают с file1.jpg, а остальные байты - до1,323,647 совпадают с file2.jpg, который был записан при втором запуске.

это содержимое файлов в шестнадцатеричном формате:

file1.jpg

0000:0000 | FF D8 FF E1  09 49 45 78  69 66 00 00  49 49 2A 00 | ÿØÿá.IExif..II*.
0000:0010 | 08 00 00 00  09 00 0F 01  02 00 06 00  00 00 7A 00 | ..............z.
...
0002:8BE0 | 56 5E 2A EC  C7 36 6D B1  57 1C D5 CD  95 8A BB 2F | V^*ìÇ6m±W.ÕÍ..»/
0002:8BF0*| 36 6C 55 AD  F2 F3 65 60  43 FF D9*                | 6lU.òóe`CÿÙ     

file2.jpg

0000:0000 | FF D8 FF E0  00 10 4A 46  49 46 00 01  01 00 00 01 | ÿØÿà..JFIF......
0000:0010 | 00 01 00 00  FF E1 01 48  45 78 69 66  00 00 49 49 | ....ÿá.HExif..II
...
0002:8BC0 | F2 07 23 D4  57 CA 7E 13  FD A9 23 B5  86 2D 3E 4D | ò.#ÔWÊ~.ý©#µ.->M
0002:8BD0 | 66 7B 58 D1  42 A3 4D 6A  57 80 38 C9  CF EB 5E 93 | f{XÑB£MjW.8ÉÏë^.
0002:8BE0 | E1 3F DA 36  CA EA 10 2E  7C 49 0B C4  E3 21 F6 8C | á?Ú6Êê..|I.Äã!ö.
0002:8BF0*| 9F D6 BB 63  8B A3 86 D5  34 B5 D9*E8  D2 E9 D7 AE | .Ö»c.£.Õ4µÙèÒé×®
0002:8C00 | B7 34 9F B5  85 18 C6 B5  DF 2E FA 6B  AD B6 5D BC | ·4.µ..Ƶß.úk.¶]¼
0002:8C10 | F7 3D 6E F3  C3 50 6B 56  32 D9 CC 14  AB AE 30 C3 | ÷=nóÃPkV2ÙÌ.«®0Ã
...
0014:3260 | E8 8B 0A CE  4E 47 AD 4A  92 B2 E4 E6  8B 3B 7F 34 | è..ÎNG.J.²äæ.;.4
0014:3270 | 1C 55 D8 6C  14 83 BA 88  AB 98 46 4D  33 FF D9    | .UØl..º.«.FM3ÿÙ 

file3.jpg (After the 3rd run)

0000:0000 | FF D8 FF E1  09 49 45 78  69 66 00 00  49 49 2A 00 | ÿØÿá.IExif..II*.
0000:0010 | 08 00 00 00  09 00 0F 01  02 00 06 00  00 00 7A 00 | ..............z.
...
0002:8BD0 | D9 B1 43 BA  E6 39 B7 CD  8A B5 97 9B  36 29 76 5E | Ù±Cºæ9·Í.µ..6)v^
0002:8BE0 | 56 5E 2A EC  C7 36 6D B1  57 1C D5 CD  95 8A BB 2F | V^*ìÇ6m±W.ÕÍ..»/
//content of file1 continues with content of file2 (Next line)
0002:8BF0*| 36 6C 55 AD  F2 F3 65 60  43 FF D9*E8  D2 E9 D7 AE | 6lU.òóe`CÿÙèÒé×®
0002:8C00 | B7 34 9F B5  85 18 C6 B5  DF 2E FA 6B  AD B6 5D BC | ·4.µ..Ƶß.úk.¶]¼
0002:8C10 | F7 3D 6E F3  C3 50 6B 56  32 D9 CC 14  AB AE 30 C3 | ÷=nóÃPkV2ÙÌ.«®0Ã
0002:8C20 | 8C F3 83 5E  55 3D 86 A1  F0 EB C5 72  E9 C6 62 E2 | .ó.^U=.¡ðëÅréÆbâ
...
0014:3260 | E8 8B 0A CE  4E 47 AD 4A  92 B2 E4 E6  8B 3B 7F 34 | è..ÎNG.J.²äæ.;.4
0014:3270 | 1C 55 D8 6C  14 83 BA 88  AB 98 46 4D  33 FF D9    | .UØl..º.«.FM3ÿÙ 

Как видно, файл3 начинается с содержимого файла file1 ипосле последних байтов файла 1 (FF D9) в третьей группе в строке 0002: 8BF0 он продолжается с содержимым файла 2 (E8 D2) ( Звездные точки )

Я протестировал процесс копированияодни и те же файлы прямо в специальной папке приложения, но результаты были правильными с правильным файлом file3 для всех трех запусков.проблема была только для SAF.

1 Ответ

0 голосов
/ 06 июля 2019

Я нашел ответ через три дня поиска и через день после того, как спросил здесь. Я не убрал вопрос, так как другие могут столкнуться с той же проблемой. Природа проблемы выводила меня на неверный путь. Он возникает не только при копировании потоков, но также при записи, например 4 байта (bbbb) для перезаписи файла 8 байтами (aaaaaaaa). он создает файл с сначала 4 новыми байтами, а затем 4 старыми байтами! (Bbbbaaaa).

Таким образом, ответ находится в FileOutputStream (). иметь размер байтов, записываемых в файл (input.channel.size ()) или (output.cannel.position ()), и обрезать оставшиеся байты (output.channel.truncate (size)).

как код, о котором идет речь, я изменил на:

contentResolver.openOutputStream(fileUri)?.use {output->
    output as FileOutputStream
    FileInputStream(File(getExternalFilesDir(null),"Pictures/file1.jpg")).use{input->
        copyStream(input, output)
        // this new line removes bytes beyond the input file size
        output.channel.truncate(input.channel.size())
        // or
        // output.channel.truncate(output.channel.position())
    }
}

Вот и все

...