BufferedWriter将随机字符串写成NULL值

后端开发 2026-07-10

我在处理遗留代码,这些代码是用Java 5和 6编写的。

最近他们把运行这段代码的机器升级到了Java 8。他们用Java 8重新编译了这段旧代码,虽然没有报错,但现在出现了奇怪的行为。

在对一个文件写入的过程中,随机地它会把字符串写成Null或空字符,在可视化文件时会产生一个空白。生成的文件是普通文本和二进制数据的混合。

它读取一张图片,在写入图片的一段之前写入一些数据。这段遗留代码多年来一直在没有改动的情况下工作,当在写入前记录日志时,这个字符串在日志中显示正确。

正如所见,它在把字符串写入后就会不断地刷新BufferedWriter,FileOutputStream没有出现错误

编辑1: 我会回答一些问题并作出一些澄清 getOutputData总是正确地生成字符串,且从不为null

生成的文件是以12455个字符为一个区块构建的

在写入文件时,STR会被任意不写入,你将得到一个这样的文件:

STR1+imageblock

STR2+imageblock

空字符串+imageblock

STR4+imageblock

我无法在本地机器上执行这段代码,必须通过流水线进行编译,并在测试机器上尝试。

在测试环境中重现这个问题(测试机的内存甚至比生产环境还少)时,我无法复现问题,文件生成正确。

它会在任意情况下发生,有些执行会毫无问题,其他则出现问题,通常也只有一行。

STR在日志中始终正确打印。 并对某些方法进行了简化。

记录日志是为了查看在写入前是否发生失败,结论是否定的,即STR总是正确形成。

报告该错误的团队可以通过复制下一条STR并改变相应的区块号来修复,因此我们知道只有这部分在出错

使用一个在线exeditor查看有问题的部分

Enter image description here

ImageInputStream isb = ImageIO.createImageInputStream(file);
Iterator<ImageReader> iterator = ImageIO.getImageReaders(isb);
if (iterator == null || !iterator.hasNext())
{
    throw new IOException("Image file format not supported by ImageIO: ");
}
ImageReader reader = (ImageReader) iterator.next();
reader.setInput(isb);
int nbPages = reader.getNumImages(true);
outputFileStream = new FileOutputStream(getPathExit(), true);
fileWriter = new BufferedWriter(new OutputStreamWriter(outputFileStream));
byte[] block = new byte[GeneratingIssuedFileConstants.BLOCK_SIZE_IMAGE];
String str = null;                  
int total=0;
for(int p=0;p<nbPages;p++)
{
    int orderNumberImg = 1;
    int bytesRead = -1;
    int offset = 0;
    total=0;
    ByteArrayOutputStream byteConte= new ByteArrayOutputStream();
    BufferedImage bufferedImage = reader.read(p);
    ImageIO.write(bufferedImage, "tif", byteConte);
    total=byteConte.size();
    ByteArrayInputStream is = new ByteArrayInputStream(byteConte.toByteArray());
    String outputData = "CONSTANT_INFO_RELATED_TO_IMAGE";
    while ((bytesRead=is.read(block, offset, GeneratingIssuedFileConstants.BLOCK_SIZE_IMAGE))> -1)
    {
        for (int z=bytesRead; z<GeneratingIssuedFileConstants.BLOCK_SIZE_IMAGE; z++)
        {
            block[z] = 0x20;
        }
        str=outputData;
        str +=String.format("%08d" ,total);
        str +=String.format("%03d" ,orderNumberImg);
        log.info("---STR VALUE---: "+str);
        fileWriter.write(str);
        fileWriter.flush();
        outputFileStream.write(block,offset,GeneratingIssuedFileConstants.BLOCK_SIZE_IMAGE);
        outputFileStream.flush();
        orderNumberImg = orderNumberImg+1;
        str="";
    }
}
outputFileStream.close();
fileWriter.close();

So when visualizing the generated file through notepadd++ this is what is shown instead of the string, there is no exception and no other error and i don't understand what is failing, lack the knowledge how to debugNotepad++ visualization of error

解决方案

One thing that leaps out at me as a major problem is combining use of outputFileStream with use of a BufferedWriter that wraps outputFileStream. They each have their own independent file pointers and their own buffering and flushing strategies, so using both will almost certainly cause the writes to clobber or corrupt each other.

Do not create a BufferedWriter or an OutputStreamWriter. Since some of your data is bytes, it all must be bytes. No writers, no characters. Fortunately, it’s not difficult to translate your text into bytes in order to write it to an OutputStream:

String str = String.format("CONSTANT_INFO_RELATED_TO_IMAGE%08d%03d",
    total, orderNumberImg);
outputFileStream.write(str.getBytes(StandardCharsets.UTF_8));

Another problem is this:

while ((bytesRead=is.read(block, offset, GeneratingIssuedFileConstants.BLOCK_SIZE_IMAGE))> -1)
    {
        for (int z=bytesRead; z<GeneratingIssuedFileConstants.BLOCK_SIZE_IMAGE; z++)

There is no guarantee how many bytes InputStream.read will actually read. bytesRead might be equal to BLOCK_SIZE_IMAGE or it might be half that or it might be 16 or it might be 1. Code must not assume anything about the returned number (except that a negative value means end of stream has been reached).

Which means the program doesn’t know in advance how many bytes are actually being filled with 0x20 in the inner loop. This may seem to work some of the time, but eventually it will not behave as the code assumes, and you will see data corruption (in the form of too many 0x20 bytes).

A better approach is to write the image directly to the OutputStream, then write the padding bytes. This would also have the advantage of using far less memory; as the code is right now, it stores a copy of each TIFF image in a BufferedImage, then again in a ByteArrayInputStream’s buffer, then stores it a third time in a byte array, then then a fourth time in block. If the image is large, that will slow down your application considerably.

try (OutputStream outputFileStream = new BufferedOutputStream(
    new FileOutputStream(getPathExit(), true)))) {

    int orderNumberImg = 1;
    for (int p = 0; p < nbPages; p++)
    {
        ByteArrayOutputStream byteConte = new ByteArrayOutputStream();
        BufferedImage bufferedImage = reader.read(p);
        ImageIO.write(bufferedImage, "tif", byteConte);
        int total = byteConte.size();

        String str = String.format("CONSTANT_INFO_RELATED_TO_IMAGE%08d%03d",
            total, orderNumberImg++);
        outputFileStream.write(str.getBytes(StandardCharsets.UTF_8));

        // No extra byte arrays needed!
        byteConte.writeTo(outputFileStream);

        for (int z = total; z < GeneratingIssuedFileConstants.BLOCK_SIZE_IMAGE; z++)
        {
            outputFileStream.write(0x20);
        }
    }
}

Some notes:

  • Always wrap a FileOutputStream in a BufferedOutputStream. Writing bytes directly to a file is extremely inefficient.
  • Putting an OutputStream in a try-with-resources statement will automatically close it. No need to call close() or flush().
  • There is no reliance on bytesRead, which removes the potential for data corruption.
  • toByteArray() is never called, and no block array is created, so there are not multiple copies of the image. ByteArrayOutputStream has a writeTo method that will transfer its bytes without making a copy of them in memory, which is especially important for potentially large data like images.
  • I moved the declaration of orderNumberImg outside the loop. I’m not certain this is correct, but if it’s inside the loop, it will always be 1。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章