java 如何使用InputStream计算校验和,然后再次使用

hpcdzsge  于 2023-01-19  发布在  Java
关注(0)|答案(3)|浏览(234)

我想计算一个给定的InputStreamCRC3``checksum,然后用它来得到字符串。

private long calculateChecksum(InputStream stream) throws IOException {
    CRC32 crc = new CRC32();
    byte[] buffer = new byte[8192];
    int length;
    while ((length = stream.read(buffer)) > 0) {
        crc.update(buffer, 0, length);
    }
    return crc.getValue();
}

然后

String text = IOUtils.toString(inputStream, UTF_8);

我也试过颠倒顺序。先把它当作字符串,然后再计算校验和。但是不起作用。
我的问题是在计算校验和的时候索引到了末尾,然后没有重置。你知道在计算完校验和之后如何使用InputStream吗?

ybzsozfc

ybzsozfc1#

正如其他人所说,一个流只能使用一次,但是您可以通过将InputStreamjava.util.zip.CheckedInputStream Package 在一起,在使用它的同时计算CRC值。
下面是一个完整的示例,假设文本文件“test.txt”位于当前目录中,并且只包含以下一行:These are german umlauts: äöüÄÖÜß

import org.apache.commons.io.IOUtils;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;

public class App {
  private static final String INPUT_FILE = "test.txt";

  public static void main( String[] args ) {
    final CRC32 crc32 = new CRC32();
    try(InputStream in = new CheckedInputStream(new BufferedInputStream(
                           new FileInputStream(INPUT_FILE)), crc32))  
    {
      final String text = IOUtils.toString(in, StandardCharsets.UTF_8);
      System.out.println(text);
      System.out.println(String.format("CRC32: %x", crc32.getValue()));
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

输出:

These are german umlauts: äöüÄÖÜß
CRC32: 84bcd851
mutmk8jj

mutmk8jj2#

InputStream是一个只读一次的流。一旦你读了它,你就不能回去重新开始。这是因为InputStream是通用的:它可以是例如从键盘读取的字节流,或者是从实时数据馈送读取的字节流。
如果输入流实际上是FileInputStream,则可以使用

inputStream.getChannel.position(0);

将其重置为文件的开头。
如果它是ByteArrayInputStream,那么你已经有了一个字节数组,所以你可以直接用它来代替。
如果你想写一个通用函数,而它不知道给定的是什么类型的InputStream,那么你可以把它 Package 在一个BufferedInputStream中,并使用它的mark()方法,这将使用额外的内存来缓冲整个流。

eit6fx6z

eit6fx6z3#

是的,InputStream被消耗。您有几个选项:

  1. mark
    mark()/reset()是输入流的可选方法;mark设置一个标记(它本身什么也不做),并重置“rewind back”到该标记,重放自上次调用mark()以来提供的所有内容。
    然而,一般的输入流要么不支持它,要么支持它,将设置标记后接收到的所有字节存储在内存中,也就是说,如果你对一个包含几GB数据的输入流这样做,你将得到一个OutOfMemoryError
    如果数据不多,就使用mark和reset, Package 一个BufferedInputStream,它支持mark/reset:
private void example(InputStream in) {
  BufferedInputStream buffered = new BufferedInputStream(in);
  in.mark();
  long crc = calculateChecksum(buffered);
  in.reset();
  String text = IOUtils.toString(buffered, UTF_8);
}

1.重复
第二种选择是复制输入流,将每个检索到的字节都发送给IOUtils和CRC算法。
这很复杂,不推荐使用。
1.改为对字符串进行校验和。
您已经有一个数据字符串。只需校验和:

private void example(InputStream in) {
  String text = IOUtils.toString(in, UTF_8);
  CRC32 crc = new CRC32();
  crc.update(text.getBytes(UTF_8));
  long checksum = crc.getValue();
}

或者,抛弃借据:

private void example(InputStream in) {
  byte[] data = in.readAllBytes();
  CRC32 crc = new CRC32();
  crc.update(data);
  long checksum = crc.getValue();
  String text = new String(data, UTF_8);
}

相关问题