Я построил метод хэширования файла в java, который принимает входное строковое представление filepath+filename
, а затем вычисляет хэш этого файла. Хешем может быть любой из поддерживаемых Java-хэшированием хэширования, таких как MD2
- SHA-512
.FileChannel ByteBuffer и Hashing Files
Я пытаюсь выполнить каждую последнюю работу, так как этот метод является неотъемлемой частью проекта, над которым я работаю. Мне рекомендовалось использовать FileChannel
вместо обычного FileInputStream
.
Мой оригинальный метод:
/**
* Gets Hash of file.
*
* @param file String path + filename of file to get hash.
* @param hashAlgo Hash algorithm to use. <br/>
* Supported algorithms are: <br/>
* MD2, MD5 <br/>
* SHA-1 <br/>
* SHA-256, SHA-384, SHA-512
* @return String value of hash. (Variable length dependent on hash algorithm used)
* @throws IOException If file is invalid.
* @throws HashTypeException If no supported or valid hash algorithm was found.
*/
public String getHash(String file, String hashAlgo) throws IOException, HashTypeException {
StringBuffer hexString = null;
try {
MessageDigest md = MessageDigest.getInstance(validateHashType(hashAlgo));
FileInputStream fis = new FileInputStream(file);
byte[] dataBytes = new byte[1024];
int nread = 0;
while ((nread = fis.read(dataBytes)) != -1) {
md.update(dataBytes, 0, nread);
}
fis.close();
byte[] mdbytes = md.digest();
hexString = new StringBuffer();
for (int i = 0; i < mdbytes.length; i++) {
hexString.append(Integer.toHexString((0xFF & mdbytes[i])));
}
return hexString.toString();
} catch (NoSuchAlgorithmException | HashTypeException e) {
throw new HashTypeException("Unsuppored Hash Algorithm.", e);
}
}
Refactored метод:
/**
* Gets Hash of file.
*
* @param file String path + filename of file to get hash.
* @param hashAlgo Hash algorithm to use. <br/>
* Supported algorithms are: <br/>
* MD2, MD5 <br/>
* SHA-1 <br/>
* SHA-256, SHA-384, SHA-512
* @return String value of hash. (Variable length dependent on hash algorithm used)
* @throws IOException If file is invalid.
* @throws HashTypeException If no supported or valid hash algorithm was found.
*/
public String getHash(String fileStr, String hashAlgo) throws IOException, HasherException {
File file = new File(fileStr);
MessageDigest md = null;
FileInputStream fis = null;
FileChannel fc = null;
ByteBuffer bbf = null;
StringBuilder hexString = null;
try {
md = MessageDigest.getInstance(hashAlgo);
fis = new FileInputStream(file);
fc = fis.getChannel();
bbf = ByteBuffer.allocate(1024); // allocation in bytes
int bytes;
while ((bytes = fc.read(bbf)) != -1) {
md.update(bbf.array(), 0, bytes);
}
fc.close();
fis.close();
byte[] mdbytes = md.digest();
hexString = new StringBuilder();
for (int i = 0; i < mdbytes.length; i++) {
hexString.append(Integer.toHexString((0xFF & mdbytes[i])));
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
throw new HasherException("Unsupported Hash Algorithm.", e);
}
}
Оба возвращают правильный хэш, однако переработан метод представляется только сотрудничать на небольших файлов. Когда я перехожу в большой файл, он полностью задыхается, и я не могу понять, почему. Я новичок в NIO
, поэтому, пожалуйста, советую.
EDIT: Вспомните, что я бросаю SHA-512 через него для тестирования.
UPDATE:
Обновление с помощью моего текущего метода.
/**
* Gets Hash of file.
*
* @param file String path + filename of file to get hash.
* @param hashAlgo Hash algorithm to use. <br/>
* Supported algorithms are: <br/>
* MD2, MD5 <br/>
* SHA-1 <br/>
* SHA-256, SHA-384, SHA-512
* @return String value of hash. (Variable length dependent on hash algorithm used)
* @throws IOException If file is invalid.
* @throws HashTypeException If no supported or valid hash algorithm was found.
*/
public String getHash(String fileStr, String hashAlgo) throws IOException, HasherException {
File file = new File(fileStr);
MessageDigest md = null;
FileInputStream fis = null;
FileChannel fc = null;
ByteBuffer bbf = null;
StringBuilder hexString = null;
try {
md = MessageDigest.getInstance(hashAlgo);
fis = new FileInputStream(file);
fc = fis.getChannel();
bbf = ByteBuffer.allocateDirect(8192); // allocation in bytes - 1024, 2048, 4096, 8192
int b;
b = fc.read(bbf);
while ((b != -1) && (b != 0)) {
bbf.flip();
byte[] bytes = new byte[b];
bbf.get(bytes);
md.update(bytes, 0, b);
bbf.clear();
b = fc.read(bbf);
}
fis.close();
byte[] mdbytes = md.digest();
hexString = new StringBuilder();
for (int i = 0; i < mdbytes.length; i++) {
hexString.append(Integer.toHexString((0xFF & mdbytes[i])));
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
throw new HasherException("Unsupported Hash Algorithm.", e);
}
}
Так что я попытался бенчмарком хэширования из MD5-файл 2.92GB, используя свой оригинальный пример и пример моего последнего изменения в. Конечно, какой-то бенчмарк является относительным, поскольку происходит кеширование ОС и дисков и другое «волшебство», которое будет искажать повторяющиеся чтения одних и тех же файлов ... но вот кадр в некоторых тестах. Я загрузил каждый метод и уволил его 5 раз после его компиляции. Тест был проведен с последнего (5-го) запуска, так как это был бы «самый горячий» запуск для этого алгоритма и любая «магия» (в моей теории в любом случае).
Here's the benchmarks so far:
Original Method - 14.987909 (s)
Latest Method - 11.236802 (s)
То есть в время, затраченное на хэш тот же файл 2.92GB 25.03% decrease
. Довольно хорошо.
Почему бы не использовать [MessageDigest.update (ByteBuffer)] (http://docs.oracle.com/javase/6/docs/api/java/security/MessageDigest.html#update%28java.nio. ByteBuffer% 29), который берет 'ByteBuffer' непосредственно вместо использования массива поддержки? – prunge
Просто хотел добавить для будущих посетителей - если вы переключитесь на использование 'ByteBuffer.allocateDirect()', то не будет резервного массива, а 'ByteBuffer.array()' будет 'fail'. Вместо этого переключитесь на использование 'MessageDigest.update (ByteBuffer)' за совет @punge. Это не только более эффективно, но и чище, чем пытаться прочитать буфер для некоторого массива, а затем передать этот массив в 'MessageDigest.update()'. – SnakeDoc
@SnakeDoc Я нашел ошибку в вашем строковом коде для хэш-номеров. Также представлена реализация с отображением памяти, которая может быть адаптирована к более крупным файлам, умножая несколько раз на файл с шагом менее 2 ГБ. – Bill