当尝试在base64中将pdf文件加载为模型引发Retrofit的字段时,我遇到了OOM异常。我知道,这不是上传文件的正常方法,但这不是第三方实现。我该如何解决此类问题?
关闭网络连接时,应用程序崩溃了
Failed to allocate a 30544558 byte allocation with 2085152 free bytes and 26MB until OOM
@Streaming
@POST("/api/order/")
fun makeOrder(@Header("Authorization") token: String, @Body order: OrderMainModel): Single<Response<PhoneNumberResponse>>
答案 0 :(得分:4)
这是一种非常疯狂的发送文件的方式。但是,尽管它是错误的(就像您通常使用@Multipart
一样),但我发现您的问题是一个有趣的练习。我的解决方案充满了技巧,但是如果您绝对确定您不会以任何方式影响API,那么也许可以帮到您。
您需要一个InputStream
。没有其他方法可以发送文件而不会最终耗尽内存。但是如何在这样的请求中发送InputStream
?
我假设OrderMainModel
有一个String
字段,可保留您的巨型Base64字符串。首先,将其更改为File
(或InputStream
本身)。
现在,假设您使用的是Gson(这是一个非常大胆的假设,但我使用的是解析器-我很确定您可以使用任何合理的json库实现类似的功能),为此创建一个自定义TypeAdapter
输入File
。 TypeAdapter
会迫使您实现此接口:
class Adapter : TypeAdapter<File>() {
override fun write(out: JsonWriter, value: File) {
// implement this
}
override fun read(`in`: JsonReader): File {
// ignore
}
}
您可以不理会read
,不需要它。
现在,在write
方法中需要做的是部分读取它,并将其连续写入JsonWriter
。嗯,无论您读什么书,都需要即时将其转换为base64。 Base64InputStream
中有一个android.util
,但似乎没有编码能力,您可以在commons-codec
中使用它:https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/binary/Base64InputStream.html(请记住传递{{ 1}}到true
的构造函数中,否则它将在解码模式下工作。)
现在,您要做的就是doEncode
包装Base64InputStream
,包裹FileInputStream
中收到的File
。
TypeAdapter
然后慢慢地将其重新写回到您的// 0 means no new lines in your Base64. null means no line separator.
Base64InputStream(FileInputStream(file)), true, 0, null)
。
但是等等,这也不容易!
JsonWriter
没有提供任何合理的方式以流方式写入单个字符串(值)。我唯一的想法就是反思。
为此,您需要检索JsonWriter
的内部JsonWriter
对象,类型为out
。然后,为了能够进行写操作而不破坏Writer
在内部保留的状态,您需要访问JsonWriter
的两个私有方法-Writer
和{ {1}}并按顺序调用它们。一切都变得非常复杂和不安全。但是,嘿,这全是有趣的,不是吗?
这是一个介绍概念的小型PoC,而不是准备就绪。 ;-)
writeDeferredValue
通过集成一些较低级别的HTTP API,您可能可以实现类似的行为,这将使您无需反射即可写入OutputStream。也许其他解析器(杰克逊,也许?)会使它更加方便。
...或者只是为API更改而战。
祝你好运!