我目前正在使用 Apache POI 创建一个 excel 文件。我想通过分段上传将此文件发送到 AWS S3 。我使用SXSSFWorkbook与所使用的替代技术相结合BigGridDemo以创建文档本身和发送的纸张数据。这就是它变得有点棘手的地方。我有一些主要工作,但由于NULs 被写入组成工作表数据的 XML 文件,我生成了一个无效的 excel 文件。
在试图追查发生这种情况的原因时,我偶然发现了这一点:
import java.io._
import java.util.zip._
val bo = new ByteArrayOutputStream()
val zo = new ZipOutputStream(bo)
zo.putNextEntry(new ZipEntry("1"))
zo.write("hello".getBytes())
zo.write("\nhello".getBytes())
val bytes1 = bo.toByteArray()
// bytes1: Array[Byte] = Array(80, 75, 3, 4, 20, 0, 8, 8, 8, 0, 107, -121, -9, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 49)
bo.reset()
zo.write("hello".getBytes())
val bytes2 = bo.toByteArray() // bytes2: Array[Byte] = Array()
zo.flush()
val bytes2 = bo.toByteArray() // bytes2: Array[Byte] = Array()
bo.size //res11: Int = 0
zo.putNextEntry() // If I make a new entry it works but I can't do this in real code...
bo.size // res17: Int = 66
似乎当我重置底层字节输出流时,它会导致 ZipOutputStream 记录任何内容。这让我很惊讶,所以我查看了 ZipOutputStream的底层源代码。我注意到默认方法是 DEFLATED,它只是调用DeflaterOutputStream#write,然后我查看了 deflater 代码本身,认为压缩算法中可能有一些我不明白的更深层次的东西,需要不重置流,或者是以某种方式受到它的影响。我找到了对FULL_FLUSH的引用并指出
如果先前的压缩数据已损坏或需要随机访问,则压缩状态将被重置,以便处理压缩输出数据的充气器可以从该点重新启动。
这对我来说听起来不错,因为我可以想象重置字节流可能会被视为损坏的数据。所以我重复了我的最小实验:
import java.io._
import java.util.zip._
val bo = new ByteArrayOutputStream()
val zo = new ZipOutputStream(bo)
zo.setLevel(Deflater.FULL_FLUSH)
zo.putNextEntry(new ZipEntry("1"))
zo.write("hello".getBytes())
val bytes1 = bo.toByteArray()
// bytes1: Array[Byte] = Array(80, 75, 3, 4, 20, 0, 8, 8, 8, 0, 84, 75, -8, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 49)
zo.flush()
bo.reset()
zo.write("\nhello".getBytes())
zo.flush()
val bytes2 = bo.toByteArray() // bytes2: Array[Byte] = Array()
所以没有骰子。我的目标是将所有内容(因此是字节数组)保留在内存中,并通过删除我已经写入 UploadPartRequest 的字节来保持内存压力较低,但这确实给事情带来了麻烦,因为我的印象是XML 文件必须压缩,因为 Excel 文件格式实际上是一个 zip 文件。我的完整代码显然有点复杂,并且使用了Play 框架和 Scala 2.12.6,我已经把它放在 github 上,如果你想查看或运行它,还添加了一些额外的注释。
相关分类