[英]Why my implementation of `InputStream` not working with `StreamingReader` from `com.monitorjbl:xlsx-streamer:2.2.0`
由於圖書館的 GitHub 網站沒有活動,我決定在這里放棄這個問題,希望得到任何支持。
我正在處理的問題是以流方式讀取 Excel 文件。 特別地,Excel 文件在使用一定的塊大小拆分為多行后,作為 blob 存儲在 SQLite 數據庫中。 例如,一個 3MB 的文件被分成三行,每行包含 1MB 的原始數據。 行按屬性排序,因此如果我 pipe 按順序將每行的 blob 列輸出到文件系統,我可以獲得 Excel 文件的副本。
由於StreamingReader
與InputStream
一起使用,我決定在 SQLite 數據庫中的這些行之上實現一個InputStream
,以便StreamingReader
直接從數據庫中讀取數據。
我首先在查詢結果之上構造一個Sequence<Byte>
,對所有 blob 列中的字節進行排序:
fun blocksByteSequence(id: String): Sequence<Byte> {
return sequence {
val conn = source.connection
val stmt = conn.createStatement()
val r = stmt.executeQuery(findFileQuery(id))
while (r.next()) yieldAll(r.getBytes(raw_data_column).asIterable())
stmt.close()
conn.close()
}
}
然后將Sequence<Byte>
轉換為InputStream
相當簡單:
class ByteSequenceInputStreamFactory(
private val seq: Sequence<Byte>,
) {
fun inputStreamProvider(): InputStream = object : InputStream() {
private val iter = seq.iterator()
override fun read(): Int {
return if (iter.hasNext()) iter.next().toInt() else -1
}
}
}
當我嘗試使用這樣的InputStream
構造StreamingReader
時出現錯誤:
val byteSeq = blocksByteSequence(id)
val ins = ByteSequenceInputStreamFactory(byteSeq).inputStreamProvider()
val reader = StreamingReader.builder().open(ins) // error
錯誤信息:
Could not open the specified zip entry source stream
org.apache.poi.openxml4j.exceptions.InvalidOperationException: Could not open the specified zip entry source stream
at app//org.apache.poi.openxml4j.opc.ZipPackage.openZipEntrySourceStream(ZipPackage.java:212)
at app//org.apache.poi.openxml4j.opc.ZipPackage.openZipEntrySourceStream(ZipPackage.java:194)
...
Caused by: java.util.zip.ZipException: invalid distances set
at org.apache.commons.compress.archivers.zip.ZipArchiveInputStream.readFromInflater(ZipArchiveInputStream.java:586)
at org.apache.commons.compress.archivers.zip.ZipArchiveInputStream.readDeflated(ZipArchiveInputStream.java:551)
...
Caused by: java.util.zip.DataFormatException: invalid distances set
at java.base/java.util.zip.Inflater.inflateBytesBytes(Native Method)
at java.base/java.util.zip.Inflater.inflate(Inflater.java:378)
...
但是,如果我將 SQLite 中的所有字節轉儲到某個路徑的 Excel 文件中:
val byteSeq = manager.blocksByteSequence(id)
val out = java.nio.file.Path.of("./private/test.xlsx")
out.outputStream().use { o -> byteSeq.forEach { o.write(it.toInt()) } }
並使用該文件生成的InputStream
,錯誤消失了。
val reader = StreamingReader.builder().open(out.inputStream())
我想我解決了這個問題。
麻煩就在這里
class ByteSequenceInputStreamFactory(
private val seq: Sequence<Byte>,
) {
fun inputStreamProvider(): InputStream = object : InputStream() {
private val iter = seq.iterator()
override fun read(): Int {
return if (iter.hasNext()) iter.next().toInt() /* this is not OK */ else -1
}
}
}
方法Byte.intoInt()
的調用沒有InputStream
預期的預期結果。
根據 Java Doc,方法InputStream.read()
從輸入 stream 讀取下一個數據字節。值字節作為0 到 255 范圍內的 int返回。 如果由於已到達 stream 的末尾而沒有可用字節,則返回值 -1。 此方法會阻塞,直到輸入數據可用、檢測到 stream 結束或拋出異常為止。
棘手的部分是,從Byte.toInt()
返回的Int
不是0 到 255 范圍內的 int 。
在 kotlin 中,一個Byte
:
表示一個 8 位簽名的 integer 。 在 JVM 上,此類型的不可空值表示為原始類型字節的值。
和Byte.toInt()
方法:
將此 Byte 值轉換為 Int。 生成的 Int 值表示與此 Byte 相同的數值。 結果 Int 值的最低有效 8 位與此 Byte 值的位相同,而最高有效 24 位填充此值的符號位。
簡單地調用Byte.toInt()
將返回此Byte
下的簽名integer。 要獲得它的0-255表示,我需要通過執行以下操作來提取租賃的 8 位有效位:
val the_0_255_int = someByte.toInt().and(0xff) // extract the last 8 bits
所以我的問題的正確代碼如下所示:
class ByteSequenceInputStreamFactory(
private val seq: Sequence<Byte>,
) {
fun inputStreamProvider(): InputStream = object : InputStream() {
private val iter = seq.iterator()
override fun read(): Int {
return if (iter.hasNext()) iter.next().toInt().and(0xff) else -1
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.