Skip to content

Commit

Permalink
Merge pull request #83 from flogy/read-file-chunks
Browse files Browse the repository at this point in the history
feat: allow reading chunks from files
  • Loading branch information
alpha0010 authored Apr 15, 2024
2 parents ddd5b76 + 2796a48 commit 7d5cbe3
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 0 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,11 @@ type ManagedFetchResult = {
- Read the content of a file.
- Default encoding of returned string is utf8.

`FileSystem.readFileChunk(path: string, offset: number, length: number, encoding?: 'utf8' | 'base64'): Promise<string>`

- Read a chunk of the content of a file, starting from byte at `offset`, reading for `length` bytes.
- Default encoding of returned string is utf8.

```
FileSystem.stat(path: string): Promise<FileStat>
Expand Down
24 changes: 24 additions & 0 deletions android/src/main/java/com/alpha0010/fs/FileAccessModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,30 @@ class FileAccessModule internal constructor(context: ReactApplicationContext) :
}
}

@ReactMethod
override fun readFileChunk(path: String, offset: Double, length: Double, encoding: String, promise: Promise) {
ioScope.launch {
try {
val data = openForReading(path).use { inputStream ->
inputStream.skip(offset.toLong())
val data = ByteArray(length.toInt())
inputStream.read(data)
data
}

val result = if (encoding == "base64") {
Base64.encodeToString(data, Base64.NO_WRAP)
} else {
data.decodeToString()
}

promise.resolve(result)
} catch (e: Throwable) {
promise.reject(e)
}
}
}

@ReactMethod
override fun stat(path: String, promise: Promise) {
ioScope.launch {
Expand Down
1 change: 1 addition & 0 deletions android/src/oldarch/FileAccessSpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ abstract class FileAccessSpec internal constructor(context: ReactApplicationCont
abstract fun mkdir(path: String, promise: Promise)
abstract fun mv(source: String, target: String, promise: Promise)
abstract fun readFile(path: String, encoding: String, promise: Promise)
abstract fun readFileChunk(path: String, offset: Double, length: Double, encoding: String, promise: Promise)
abstract fun stat(path: String, promise: Promise)
abstract fun statDir(path: String, promise: Promise)
abstract fun unlink(path: String, promise: Promise)
Expand Down
14 changes: 14 additions & 0 deletions ios/FileAccess.mm
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ - (void)ls:(NSString * _Nonnull)path withResolver:(RCTPromiseResolveBlock _Nonnu
- (void)mkdir:(NSString * _Nonnull)path withResolver:(RCTPromiseResolveBlock _Nonnull)resolve withRejecter:(RCTPromiseRejectBlock _Nonnull)reject;
- (void)mv:(NSString * _Nonnull)source withTarget:(NSString * _Nonnull)target withResolver:(RCTPromiseResolveBlock _Nonnull)resolve withRejecter:(RCTPromiseRejectBlock _Nonnull)reject;
- (void)readFile:(NSString * _Nonnull)path withEncoding:(NSString * _Nonnull)encoding withResolver:(RCTPromiseResolveBlock _Nonnull)resolve withRejecter:(RCTPromiseRejectBlock _Nonnull)reject;
- (void)readFileChunk:(NSString * _Nonnull)path withOffset:(NSNumber * _Nonnull)offset withLength:(NSNumber * _Nonnull)length withEncoding:(NSString * _Nonnull)encoding withResolver:(RCTPromiseResolveBlock _Nonnull)resolve withRejecter:(RCTPromiseRejectBlock _Nonnull)reject;
- (void)stat:(NSString * _Nonnull)path withResolver:(RCTPromiseResolveBlock _Nonnull)resolve withRejecter:(RCTPromiseRejectBlock _Nonnull)reject;
- (void)statDir:(NSString * _Nonnull)path withResolver:(RCTPromiseResolveBlock _Nonnull)resolve withRejecter:(RCTPromiseRejectBlock _Nonnull)reject;
- (void)unlink:(NSString * _Nonnull)path withResolver:(RCTPromiseResolveBlock _Nonnull)resolve withRejecter:(RCTPromiseRejectBlock _Nonnull)reject;
Expand Down Expand Up @@ -170,6 +171,19 @@ - (instancetype)init
[impl readFile:path withEncoding:encoding withResolver:resolve withRejecter:reject];
}

RCT_EXPORT_METHOD(readFileChunk:(NSString *)path
offset:(double)offset
length:(double)length
encoding:(NSString *)encoding
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
{
NSNumber *offsetNumber = @(offset);
NSNumber *lengthNumber = @(length);
[impl readFileChunk:path withOffset:offsetNumber withLength:lengthNumber withEncoding:encoding withResolver:resolve withRejecter:reject];
}


RCT_EXPORT_METHOD(stat:(NSString *)path
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
Expand Down
27 changes: 27 additions & 0 deletions ios/FileAccess.swift
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,33 @@ public class FileAccess : NSObject {
}
}

@objc(readFileChunk:withOffset:withLength:withEncoding:withResolver:withRejecter:)
public func readFileChunk(path: String, offset: NSNumber, length: NSNumber, encoding: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
DispatchQueue.global().async {
do {
let fileHandle = try FileHandle(forReadingFrom: URL(fileURLWithPath: path.path()))
defer {
fileHandle.closeFile()
}

fileHandle.seek(toFileOffset: UInt64(truncating: offset))
let binaryData = fileHandle.readData(ofLength: Int(truncating: length))

if encoding == "base64" {
resolve(binaryData.base64EncodedString())
} else {
if let content = String(data: binaryData, encoding: .utf8) {
resolve(content)
} else {
reject("ERR", "Failed to decode content from file '\(path)' with specified encoding.", nil)
}
}
} catch {
reject("ERR", "Failed to read '\(path)'. \(error.localizedDescription)", error)
}
}
}

@objc(stat:withResolver:withRejecter:)
public func stat(path: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
DispatchQueue.global().async {
Expand Down
6 changes: 6 additions & 0 deletions src/NativeFileAccess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ export interface Spec extends TurboModule {
mkdir(path: string): Promise<string>;
mv(source: string, target: string): Promise<void>;
readFile(path: string, encoding: string): Promise<string>;
readFileChunk(
path: string,
offset: number,
length: number,
encoding: string
): Promise<string>;
stat(path: string): Promise<FileStat>;
statDir(path: string): Promise<FileStat[]>;
unlink(path: string): Promise<void>;
Expand Down
12 changes: 12 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,18 @@ export const FileSystem = {
return FileAccessNative.readFile(path, encoding);
},

/**
* Read a chunk of the content of a file.
*/
readFileChunk(
path: string,
offset: number,
length: number,
encoding: Encoding = 'utf8'
) {
return FileAccessNative.readFileChunk(path, offset, length, encoding);
},

/**
* Read file metadata.
*/
Expand Down

0 comments on commit 7d5cbe3

Please sign in to comment.