Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/excel/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 将Excel文件流式传输到内存中的S3?为什么ZipOutputStream上的ByteOutputStream.reset()会导致写入无法工作?_Java_Excel_Scala_Amazon S3_Apache Poi - Fatal编程技术网

Java 将Excel文件流式传输到内存中的S3?为什么ZipOutputStream上的ByteOutputStream.reset()会导致写入无法工作?

Java 将Excel文件流式传输到内存中的S3?为什么ZipOutputStream上的ByteOutputStream.reset()会导致写入无法工作?,java,excel,scala,amazon-s3,apache-poi,Java,Excel,Scala,Amazon S3,Apache Poi,我目前正在使用ApachePOI创建一个excel文件。我想通过将此文件发送到AWS S3。 我将使用与所使用的替换技术相结合的方法来创建文档本身并发送工作表数据。这就是它变得有点棘手的地方。我有一些基本正常工作的东西,但是由于NULs被写入到构成工作表数据的XML文件中,我正在生成一个无效的excel文件 在试图找出这种情况发生的原因时,我偶然发现: import java.io._ import java.util.zip._ val bo = new ByteArrayOutputStre

我目前正在使用ApachePOI创建一个excel文件。我想通过将此文件发送到AWS S3。 我将使用与所使用的替换技术相结合的方法来创建文档本身并发送工作表数据。这就是它变得有点棘手的地方。我有一些基本正常工作的东西,但是由于
NUL
s被写入到构成工作表数据的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不再写任何东西。这让我很惊讶,所以我去调查了一下。我注意到默认方法是DEFLATED,它只调用DEFLATED,然后我查看deflater代码本身,认为可能在压缩算法中有更深层次的东西,我不理解它要求流不被重置,或者受到它的影响。我找到了一个参考,并注意到

压缩状态被重置,以便在先前的压缩数据已损坏或需要随机访问时,使用压缩输出数据的充气机可以从此点重新启动

这听起来不错,因为我可以想象重置字节流可能会被视为损坏的数据。所以我重复了我的最小实验:

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文件。我的完整代码显然有点复杂,使用的是和Scala 2.12.6

我知道我可以通过先将excel文件写入磁盘,然后再上传来完成将该文件部分上传到s3,但出于我的目的,我希望有一个全内存解决方案,这样在生成大型临时文件时,我就不必处理web服务器上的磁盘空间问题。通过将生成的行保持在上传状态,我认为每次上传时内存压力应该保持相当恒定。以下是当前代码在xml文件表数据中生成的内容:

这对我来说意味着,尽管我的实验没有显示任何字节,但在某个点上,由于NUL最终结束,更多的字节发生并写入文件


所以。。。为什么会发生这种情况?为什么
ByteArrayOutputStream.reset()
会导致写入
zipoutstream
时出现问题?如果我不调用.reset(),那么
ByteArrayOutputStream
似乎会一直扩展到非常大并导致内存不足错误?或者,既然数据正在被压缩,我应该不担心吗?

请查看贝尔实验室提供的PDF《提高Java服务器性能的实用指南》:

它讨论了一切,包括重置方法的使用

另外,请看一下这篇文章:

最后,对于内存不足等问题,您应该始终进行尝试/捕获

如果我不调用.reset(),ByteArrayOutputStream似乎会 扩展到巨大并导致内存不足错误


让我知道这是否有用。

我不认为这是
ByteArrayOutputStream.reset()的错误。

Cipherstream
和其他过滤流类似,
DeflaterOutputStream
因此
zipoutStream
实际上不会写入底层流(您的
ByteArrayOutputStream
),直到它能够/需要写入(有时甚至在刷新时)

我相信在这种情况下,
ZipInputStream
可能只在某些块大小或关闭
ZipEntry
时写入底层流;不太确定,但这是我的猜测

例如:

val bo = new ByteArrayOutputStream()
val zo = new ZipOutputStream(bo)
zo.putNextEntry(new ZipEntry("example entry"))

// v prints the entry header bytes v
println(bo.toString())

zo.write("hello".getBytes())
zo.flush();

// v still only the entry header bytes v
println(bo.toString())

excelfstreamingtos3service-第155行
中,我注意到一件事,您可能希望更改为
zos.write(byteBuffer、offset、offset+bytesRead)
,或者类似的内容。写入完整的缓冲区肯定是导致所有这些
NUL
字符的原因,因为您的缓冲区在读取期间可能没有被填满,并且仍然有许多空索引。毕竟,xml看起来像是从
NUL
之前的地方继续往下看:
所以看起来你确实在写所有的数据,只是在其中穿插
NUL
s.

关于
zos.write
你完全正确!我将其更改为
if(bytesRead!=-1){zos.write(byteBuffer,0,bytesRead)}
,并且没有NUL,可以轻松打开excel文件!还有,在它有意义之前它不会写,我想,听起来它应该受到同花顺行为的影响,对吗?如果将其设置为更具攻击性的刷新设置,是否仍然只能获取标题?是的,某些刷新行为可能会影响对底层流的写入。例如,
DeflaterOutputStream.synchFlush
为true时,
DeflaterOutputStream.flush()
将写入基础流。然而,似乎没有API来实际设置
ZipOutputStream
的刷新行为,您对
zo.setLevel(Deflater.FULL_flush)
的调用是不正确的,因为
level
指的是压缩级别,而不是刷新级别。
setLevel()
的这种类型不安全是枚举远远优于int常量的原因之一…如果您仍要设置刷新行为,然后,您必须复制粘贴并重新实现
ZipOutputStream
,并可能为
DeflatedOutputStream
passing
true
调用超级构造函数以传递
synchFlush
参数,或者添加API以将刷新行为传递到
Def