有没有办法使用 Guava 获取 InputStream 的哈希码?

Posted

技术标签:

【中文标题】有没有办法使用 Guava 获取 InputStream 的哈希码?【英文标题】:Is there any way to get the hashcode of an InputStream using Guava? 【发布时间】:2019-06-12 20:18:42 【问题描述】:

有没有办法在 Java 中获取 InputStream 的 HashCode, 我正在尝试使用 PrimeFaces 中的<p:fileUpload/> 上传图片,将其转换为 HashCode 并将其与另一张图片进行比较。

目前我正在尝试这个:

public void save(FileUploadEvent event) throws IOException 
        HashCode hashCode = null;
        HashCode hashCodeCompare = null;
        hashCode = Files.asByteSource(new File(event.toString())).hash(Hashing.murmur3_128(50));
        hashCodeCompare = Files.asByteSource(new File(FilePathOfFileToCompare)).hash(Hashing.murmur3_128(50));
        boolean hashTrueFalse;
        if(hashCode.equals(hashCodeCompare)) 
            System.out.println("true");
         else
            System.out.println("false");
        

        try (InputStream input = event.getFile().getInputstream()) 
            String imageName = generateFileName() + "." + fileExtensions(event.getFile().getFileName());
            String imageLink = PICTURE_DESTINATION + "\\" + imageName;


            Picture picture = new Picture();
            picture.setPictureUrl(imageLink);
            pictureService.createOrUpdate(picture);

            personForm.getCurrentPersonDTO().setPictureDTO(pictureMapper.toDTO(picture));


         catch (IOException e) 
            e.printStackTrace();
        
    

有什么方法可以将InputStream 变成哈希码?

【问题讨论】:

等等,你是要获取流的HashCode还是图像的HashCode?你当然可以通过调用.hashCode() 来获取任何Java 对象的HashCode,但更重要的是int 代表什么以及为什么要使用它。 我尝试上传一张图片,并将其转为hashvalue,并与另一张图片进行比较,看是否已经存在。我尝试使用.hashCode(),但如果我尝试将相同的图像与输入流和文件进行比较,它会给我一个不同的 hashCode 如果您尝试使用 HashCode 来比较图像,如果图像是 JPG 或其他使用有损压缩存储的图像怎么办?图像文件位可能完全不同,但图像本身在功能上可能相同。 那么,您首选的比较图片上传和实际图片的方法是什么?我只需要一些来自外部的 iedeas/输入,因为我现在在这个问题上工作了 2.5 小时 D: 将 InputStream 读入 byte[] 然后在字节数组上运行哈希函数。您可能不想为此使用 hashCode() 。请改用 SHA256。 【参考方案1】:

我建议使用Files.asByteSource(fileSource.getFile()).hash(hashFunction).padToLong()

【讨论】:

【参考方案2】:

如果要计算其包含的字节的哈希值,则必须读取 InputStream。首先将 InputSteam 读取到 byte[]。

在 Guava 中使用 ByteStreams:

InputStream in = ...;
byte[] bytes = ByteStreams.toByteArray(in);

另一种流行的方法是使用Commons IO:

InputStream in = ...;
byte[] bytes = IOUtils.toByteArray(in);

然后你可以在字节数组上调用Arrays.hashCode():

int hash = java.util.Arrays.hashCode(bytes);

但是,您可能会考虑使用 SHA256 作为哈希函数,因为您不太可能发生冲突:

MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] sha256Hash = digest.digest(bytes);

如果您不想将整个流读取到内存字节数组中,您可以在其他人读取 InputStream 时计算哈希值。例如,您可能希望将 InputStream 流式传输到磁盘到数据库中。 Guava 提供了一个封装了 InputStream 的类,它会为您执行此操作HashingInputStream:

首先用 HashinInputStream 包装你的 InputStream

HashingInputStream hin = new HashingInputStream(Hashing.sha256(), in);

然后让 HashingInputStream 以你喜欢的任何方式读取

while(hin.read() != -1);

然后从 HashingInputStream 中获取哈希

byte[] sha256Hash = hin.hash().asBytes();

【讨论】:

【参考方案3】:

你想要做的是ByteStreams.copy(input, Funnels.asOutputStream(hasher)),其中hasher是从例如获取的。 Hashing.sha256().newHasher()。然后,调用hasher.hash() 得到结果HashCode

【讨论】:

我不知道为什么这没有更多的赞成票,大多数其他解决方案都需要将输入流复制到一个字节[],或者在哈希器之外循环输入流。 ByteStreams.copy(in, out) 正在为您循环输入流。使用此解决方案的缺点是您将哈希器用作接收器。因此,当您散列它们时,您会丢失字节。这通常是不可取的,例如您正在处理用户刚刚上传的输入流。你们都想将字节传递到存储中并生成哈希。但是这里的字节会在从 InputStream 中读取时丢失。 @bhspencer:老实说,实际上在大多数情况下,我实际上并不想保留字节。

以上是关于有没有办法使用 Guava 获取 InputStream 的哈希码?的主要内容,如果未能解决你的问题,请参考以下文章

Guava手记

使用 Guava 达到极限的 Android 项目

面试专栏Guava - ListenableFuture,避免Future获取阻塞问题,增加回调

Google Guava:支持获取符合给定谓词的集合元素的方法?

Guava Cache 过期回源

Guava官方文档-RateLimiter类