diff --git a/README.md b/README.md index 7680fab86..09a964c44 100644 --- a/README.md +++ b/README.md @@ -594,6 +594,7 @@ File Access APIs - [readFile (0.6.0)](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#readfilepath-encodingpromise) - [readStream](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#readstreampath-encoding-buffersizepromise) - [writeStream](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#writestreampathstring-encodingstring-appendbooleanpromise) +- [hash](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#hashpath-algorithmpromise) - [unlink](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#unlinkpathstringpromise) - [mkdir](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#mkdirpathstringpromise) - [ls](https://github.com/wkh237/react-native-fetch-blob/wiki/File-System-Access-API#lspathstringpromise) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java index 19e1be435..68cd1d5c3 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java @@ -267,11 +267,21 @@ public void run() { } @ReactMethod + public void hash(final String path, final String algorithm, final Promise promise) { + threadPool.execute(new Runnable() { + @Override + public void run() { + RNFetchBlobFS.hash(path, algorithm, promise); + } + }); + } + /** * @param path Stream file path * @param encoding Stream encoding, should be one of `base64`, `ascii`, and `utf8` * @param bufferSize Stream buffer size, default to 4096 or 4095(base64). */ + @ReactMethod public void readStream(final String path, final String encoding, final int bufferSize, final int tick, final String streamId) { final ReactApplicationContext ctx = this.getReactApplicationContext(); fsThreadPool.execute(new Runnable() { diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java index 15be72e7e..bfcd9b588 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java @@ -31,6 +31,7 @@ import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; +import java.security.MessageDigest; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -696,6 +697,52 @@ public void onScanCompleted(String s, Uri uri) { } } + static void hash(String path, String algorithm, Promise promise) { + try { + Map algorithms = new HashMap<>(); + + algorithms.put("md5", "MD5"); + algorithms.put("sha1", "SHA-1"); + algorithms.put("sha224", "SHA-224"); + algorithms.put("sha256", "SHA-256"); + algorithms.put("sha384", "SHA-384"); + algorithms.put("sha512", "SHA-512"); + + if (!algorithms.containsKey(algorithm)) throw new Exception("Invalid hash algorithm"); + + File file = new File(path); + + if (file.isDirectory()) { + promise.reject("hash error", "EISDIR: illegal operation on a directory, read"); + return; + } + + if (!file.exists()) { + promise.reject("hash error", "ENOENT: no such file or directory, open '" + path + "'"); + return; + } + + MessageDigest md = MessageDigest.getInstance(algorithms.get(algorithm)); + + FileInputStream inputStream = new FileInputStream(path); + byte[] buffer = new byte[(int)file.length()]; + + int read; + while ((read = inputStream.read(buffer)) != -1) { + md.update(buffer, 0, read); + } + + StringBuilder hexString = new StringBuilder(); + for (byte digestByte : md.digest()) + hexString.append(String.format("%02x", digestByte)); + + promise.resolve(hexString.toString()); + } catch (Exception ex) { + ex.printStackTrace(); + promise.reject("hash error", ex.getLocalizedMessage()); + } + } + /** * Create new file at path * @param path The destination path of the new file. diff --git a/fs.js b/fs.js index 70ed39d96..7423c2d01 100644 --- a/fs.js +++ b/fs.js @@ -239,6 +239,15 @@ function scanFile(pairs:any):Promise { }) } +function hash(path: string, algorithm: string): Promise { + if(typeof path !== 'string') + return Promise.reject(new Error('Invalid argument "path" ')) + if(typeof algorithm !== 'string') + return Promise.reject(new Error('Invalid argument "algorithm" ')) + + return RNFetchBlob.hash(path, algorithm) +} + function cp(path:string, dest:string):Promise { return new Promise((resolve, reject) => { RNFetchBlob.cp(path, dest, (err, res) => { @@ -379,6 +388,7 @@ export default { appendFile, pathForAppGroup, readFile, + hash, exists, createFile, isDir, diff --git a/ios/RNFetchBlob/RNFetchBlob.m b/ios/RNFetchBlob/RNFetchBlob.m index 5eee349b2..78b1cd4bb 100644 --- a/ios/RNFetchBlob/RNFetchBlob.m +++ b/ios/RNFetchBlob/RNFetchBlob.m @@ -461,6 +461,15 @@ - (NSDictionary *)constantsToExport }]; } +#pragma mark - fs.hash +RCT_EXPORT_METHOD(hash:(NSString *)path + algorithm:(NSString *)algorithm + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + [RNFetchBlobFS hash:path algorithm:[NSString stringWithString:algorithm] resolver:resolve rejecter:reject]; +} + #pragma mark - fs.readStream RCT_EXPORT_METHOD(readStream:(NSString *)path withEncoding:(NSString *)encoding bufferSize:(int)bufferSize tick:(int)tick streamId:(NSString *)streamId) { diff --git a/ios/RNFetchBlobFS.m b/ios/RNFetchBlobFS.m index 183569e35..89148396b 100644 --- a/ios/RNFetchBlobFS.m +++ b/ios/RNFetchBlobFS.m @@ -489,6 +489,76 @@ + (void) readFile:(NSString *)path }]; } +# pragma mark - hash + +RCT_EXPORT_METHOD(hash:(NSString *)filepath + algorithm:(NSString *)algorithm + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filepath]; + + if (!fileExists) { + return reject(@"ENOENT", [NSString stringWithFormat:@"ENOENT: no such file or directory, open '%@'", filepath], nil); + } + + NSError *error = nil; + + NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filepath error:&error]; + + if (error) { + return [self reject:reject withError:error]; + } + + if ([attributes objectForKey:NSFileType] == NSFileTypeDirectory) { + return reject(@"EISDIR", @"EISDIR: illegal operation on a directory, read", nil); + } + + NSData *content = [[NSFileManager defaultManager] contentsAtPath:filepath]; + + NSArray *keys = [NSArray arrayWithObjects:@"md5", @"sha1", @"sha224", @"sha256", @"sha384", @"sha512", nil]; + + NSArray *digestLengths = [NSArray arrayWithObjects: + @CC_MD5_DIGEST_LENGTH, + @CC_SHA1_DIGEST_LENGTH, + @CC_SHA224_DIGEST_LENGTH, + @CC_SHA256_DIGEST_LENGTH, + @CC_SHA384_DIGEST_LENGTH, + @CC_SHA512_DIGEST_LENGTH, + nil]; + + NSDictionary *keysToDigestLengths = [NSDictionary dictionaryWithObjects:digestLengths forKeys:keys]; + + int digestLength = [[keysToDigestLengths objectForKey:algorithm] intValue]; + + if (!digestLength) { + return reject(@"Error", [NSString stringWithFormat:@"Invalid hash algorithm '%@'", algorithm], nil); + } + + unsigned char buffer[digestLength]; + + if ([algorithm isEqualToString:@"md5"]) { + CC_MD5(content.bytes, (CC_LONG)content.length, buffer); + } else if ([algorithm isEqualToString:@"sha1"]) { + CC_SHA1(content.bytes, (CC_LONG)content.length, buffer); + } else if ([algorithm isEqualToString:@"sha224"]) { + CC_SHA224(content.bytes, (CC_LONG)content.length, buffer); + } else if ([algorithm isEqualToString:@"sha256"]) { + CC_SHA256(content.bytes, (CC_LONG)content.length, buffer); + } else if ([algorithm isEqualToString:@"sha384"]) { + CC_SHA384(content.bytes, (CC_LONG)content.length, buffer); + } else if ([algorithm isEqualToString:@"sha512"]) { + CC_SHA512(content.bytes, (CC_LONG)content.length, buffer); + } else { + return reject(@"Error", [NSString stringWithFormat:@"Invalid hash algorithm '%@'", algorithm], nil); + } + + NSMutableString *output = [NSMutableString stringWithCapacity:digestLength * 2]; + for(int i = 0; i < digestLength; i++) + [output appendFormat:@"%02x",buffer[i]]; + + resolve(output); +} # pragma mark - mkdir