- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
>>背景
我想使用 SAF(存储访问框架)将我的应用程序的数据文件保存到存储介质上用户所需的位置。我首先在应用程序专用文件夹中创建文件,然后将其复制到用户从文件选择器对话框中选择的文件(代码稍后提供)。
此过程非常适合新文件,但对于现有文件,尽管文件选择器警告覆盖文件,但最终文件在写入前不会被删除。
通过计算写入的字节数并使用十六进制编辑器调查文件,代码将正确的字节写入输出流但是:如果现有文件的字节数多于要写入的字节数,则最终覆盖的文件已损坏(实际上并没有损坏,请参阅下一节进行说明)并且如果现有的字节数少于要写入的字节数,则最终覆盖的文件是正确的。
>> 更多细节和代码
我使用下面的代码来显示问题(jpg 是示例):我将尝试使用两个文件:
file1.jpg 166,907 bytes
file2.jpg 1,323,647 bytes
file3.jpg The final file with variable size
首先,我会将文件 1 复制到用户选择的名为文件 3 的文件夹(目标文件),然后用文件 2 覆盖它,最后我将用文件 1 再次覆盖它。查看代码是什么以及会发生什么:
调用文件选择器的代码:
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 完全一样。 file3.jpg 在第二次运行时也与 file2.jpg 相同。但是在用 file1.jpg 的内容覆盖 file3.jpg 的第三次运行(比 file3.jpg 有 kess 字节)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ÿÙ
如您所见,file3 以 file1 的内容开始,在第 0002:8BF0 行的第三组 file1 (FF D9) 的最后字节之后,它继续 file2 (E8 D2) 的内容(星点)
我测试了直接在应用程序的专用文件夹中复制相同文件的过程,但结果是正确的,所有三个运行的 file3 都是正确的。问题只是针对 SAF。
最佳答案
经过三天的搜索和在这里提问一天后,我找到了答案。我没有删除这个问题,因为其他人可能会遇到同样的问题。问题的本质是让我走错了路。它不仅出现在复制流中,而且出现在编写例如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())
}
}
2019/09/15 更新
感谢来自 @mjanssen 的第一条评论您也可以通过在复制文件 copyStream(input, output)
之前放置 output.channel.truncate(0)
来获得相同的结果:
contentResolver.openOutputStream(fileUri)?.use {
output-> output as FileOutputStream
FileInputStream(File(getExternalFilesDir(null),"Pictures/file1.jpg")).use{input->
output.channel.truncate(0)
copyStream(input, output)
}
}
就是这样
关于android - 如何使用android存储访问框架正确覆盖文件内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56902845/
我最近在/ drawable中添加了一些.gifs,以便可以将它们与按钮一起使用。这个工作正常(没有错误)。现在,当我重建/运行我的应用程序时,出现以下错误: Error: Gradle: Execu
Android 中有返回内部存储数据路径的方法吗? 我有 2 部 Android 智能手机(Samsung s2 和 s7 edge),我在其中安装了一个应用程序。我想使用位于这条路径中的 sqlit
这个问题在这里已经有了答案: What's the difference between "?android:" and "@android:" in an android layout xml f
我只想知道 android 开发手机、android 普通手机和 android root 手机之间的实际区别。 我们不能从实体店或除 android marketplace 以外的其他地方购买开发手
自Gradle更新以来,我正在努力使这个项目达到标准。这是一个团队项目,它使用的是android-apt插件。我已经进行了必要的语法更改(编译->实现和apt->注释处理器),但是编译器仍在告诉我存在
我是android和kotlin的新手,所以请原谅要解决的一个非常简单的问题! 我已经使用导航体系结构组件创建了一个基本应用程序,使用了底部的导航栏和三个导航选项。每个导航选项都指向一个专用片段,该片
我目前正在使用 Facebook official SDK for Android . 我现在正在使用高级示例应用程序,但我不知道如何让它获取应用程序墙/流/状态而不是登录的用户。 这可能吗?在那种情
我在下载文件时遇到问题, 我可以在模拟器中下载文件,但无法在手机上使用。我已经定义了上网和写入 SD 卡的权限。 我在服务器上有一个 doc 文件,如果用户单击下载。它下载文件。这在模拟器中工作正常但
这个问题在这里已经有了答案: What is the difference between gravity and layout_gravity in Android? (22 个答案) 关闭 9
任何人都可以告诉我什么是 android 缓存和应用程序缓存,因为当我们谈论缓存清理应用程序时,它的作用是,缓存清理概念是清理应用程序缓存还是像内存管理一样主存储、RAM、缓存是不同的并且据我所知,缓
假设应用程序 Foo 和 Eggs 在同一台 Android 设备上。任一应用程序都可以获取设备上所有应用程序的列表。一个应用程序是否有可能知道另一个应用程序是否已经运行以及运行了多长时间? 最佳答案
我有点困惑,我只看到了从 android 到 pc 或者从 android 到 pc 的例子。我需要制作一个从两部手机 (android) 连接的 android 应用程序进行视频聊天。我在想,我知道
用于使用 Android 以编程方式锁定屏幕。我从 Stackoverflow 之前关于此的问题中得到了一些好主意,并且我做得很好,但是当我运行该代码时,没有异常和错误。而且,屏幕没有锁定。请在这段代
文档说: android:layout_alignParentStart If true, makes the start edge of this view match the start edge
我不知道这两个属性和高度之间的区别。 以一个TextView为例,如果我将它的layout_width设置为wrap_content,并将它的width设置为50 dip,会发生什么情况? 最佳答案
这两个属性有什么关系?如果我有 android:noHistory="true",那么有 android:finishOnTaskLaunch="true" 有什么意义吗? 最佳答案 假设您的应用中有
我是新手,正在尝试理解以下 XML 代码: 查看 developer.android.com 上的文档,它说“starStyle”是 R.attr 中的常量, public static final
在下面的代码中,为什么当我设置时单选按钮的外观会发生变化 android:layout_width="fill_parent" 和 android:width="fill_parent" 我说的是
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 9
假设我有一个函数 fun myFunction(name:String, email:String){},当我调用这个函数时 myFunction('Ali', 'ali@test.com ') 如何
我是一名优秀的程序员,十分优秀!