rn-file-toolkit 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -137,7 +137,7 @@ RCT_EXPORT_MODULE()
137
137
  }
138
138
 
139
139
  BOOL isBackground = [options[@"background"] boolValue];
140
- NSString *downloadId = [self generateDownloadId];
140
+ NSString *downloadId = options[@"downloadId"] ?: [self generateDownloadId];
141
141
  NSURL *url = [NSURL URLWithString:urlString];
142
142
  NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
143
143
 
@@ -376,6 +376,14 @@ RCT_EXPORT_MODULE()
376
376
  return;
377
377
  }
378
378
 
379
+ // Safety check: reject files > 50MB to prevent crashing the RN bridge
380
+ NSDictionary *attrs = [fm attributesOfItemAtPath:filePath error:nil];
381
+ unsigned long long fileSize = [attrs fileSize];
382
+ if (fileSize > 50 * 1024 * 1024) {
383
+ resolve(@{@"success": @NO, @"error": @"File exceeds 50MB limit for readFile. Use streaming or base64 encoding for large files."});
384
+ return;
385
+ }
386
+
379
387
  NSError *readError = nil;
380
388
  NSData *raw = [NSData dataWithContentsOfFile:filePath options:0 error:&readError];
381
389
  if (!raw || readError) {
@@ -651,9 +659,14 @@ didCompleteWithError:(NSError *)error {
651
659
  if (uploadId) {
652
660
  NSDictionary *funcs = self.uploadPromises[uploadId];
653
661
  RCTPromiseResolveBlock uploadResolve = funcs[@"resolve"];
662
+ NSString *tempFile = funcs[@"tempFile"];
663
+
664
+ if (tempFile) {
665
+ [[NSFileManager defaultManager] removeItemAtPath:tempFile error:nil];
666
+ }
654
667
 
655
668
  if (error) {
656
- if (uploadResolve) uploadResolve(@{@"success": @NO, @"error": error.localizedDescription});
669
+ if (uploadResolve) uploadResolve(@{@"success": @NO, @"error": error.localizedDescription, @"uploadId": uploadId});
657
670
  } else {
658
671
  NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
659
672
  NSData *responseData = self.uploadResponseData[uploadId] ?: [NSData data];
@@ -662,7 +675,8 @@ didCompleteWithError:(NSError *)error {
662
675
  if (uploadResolve) uploadResolve(@{
663
676
  @"success": @(httpResponse.statusCode >= 200 && httpResponse.statusCode < 300),
664
677
  @"status": @(httpResponse.statusCode),
665
- @"data": respString
678
+ @"data": respString,
679
+ @"uploadId": uploadId
666
680
  });
667
681
  }
668
682
 
@@ -778,7 +792,7 @@ totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
778
792
  if (totalBytesExpectedToSend > 0) {
779
793
  int progress = (int)((totalBytesSent * 100) / totalBytesExpectedToSend);
780
794
  [self sendEventWithName:@"onUploadProgress"
781
- body:@{@"url": url, @"progress": @(progress)}];
795
+ body:@{@"url": url, @"uploadId": uploadId, @"progress": @(progress)}];
782
796
  }
783
797
  }
784
798
 
@@ -822,6 +836,7 @@ totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
822
836
  return;
823
837
  }
824
838
 
839
+ NSString *uploadId = options[@"uploadId"] ?: [self generateDownloadId];
825
840
  NSString *fieldName = options[@"fieldName"] ?: @"file";
826
841
  NSDictionary *headers = options[@"headers"];
827
842
  NSDictionary *params = options[@"parameters"];
@@ -837,28 +852,63 @@ totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
837
852
  }
838
853
  }
839
854
 
840
- NSMutableData *body = [NSMutableData data];
855
+ // Create a temporary file to avoid OutOfMemory crash for large uploads
856
+ NSString *tempFileName = [NSString stringWithFormat:@"upload_%@.tmp", [[NSUUID UUID] UUIDString]];
857
+ NSString *tempFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:tempFileName];
858
+
859
+ [[NSFileManager defaultManager] createFileAtPath:tempFilePath contents:nil attributes:nil];
860
+ NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:tempFilePath];
861
+ if (!fileHandle) {
862
+ resolve(@{@"success": @NO, @"error": @"Failed to create temp file for upload"});
863
+ return;
864
+ }
865
+
866
+ NSMutableData *preamble = [NSMutableData data];
841
867
  [params enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
842
- [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
843
- [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
844
- [body appendData:[[NSString stringWithFormat:@"%@\r\n", value] dataUsingEncoding:NSUTF8StringEncoding]];
868
+ [preamble appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
869
+ [preamble appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
870
+ [preamble appendData:[[NSString stringWithFormat:@"%@\r\n", value] dataUsingEncoding:NSUTF8StringEncoding]];
845
871
  }];
846
872
 
847
873
  NSString *fileName = [filePath lastPathComponent];
848
- [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
849
- [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", fieldName, fileName] dataUsingEncoding:NSUTF8StringEncoding]];
850
- [body appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
851
- [body appendData:[NSData dataWithContentsOfFile:filePath]];
852
- [body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
853
- [body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
854
-
855
- // Use delegate-based session for upload progress support
856
- NSString *uploadId = [self generateDownloadId];
857
- NSURLSessionUploadTask *task = [self.fgSession uploadTaskWithRequest:request fromData:body];
874
+ [preamble appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
875
+ [preamble appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", fieldName, fileName] dataUsingEncoding:NSUTF8StringEncoding]];
876
+ [preamble appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
877
+
878
+ [fileHandle writeData:preamble];
879
+
880
+ // Stream the actual file content to the temp file
881
+ NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:filePath];
882
+ [inputStream open];
883
+ uint8_t buffer[32768]; // 32KB chunks
884
+ while ([inputStream hasBytesAvailable]) {
885
+ NSInteger bytesRead = [inputStream read:buffer maxLength:sizeof(buffer)];
886
+ if (bytesRead > 0) {
887
+ [fileHandle writeData:[NSData dataWithBytes:buffer length:bytesRead]];
888
+ } else if (bytesRead < 0) {
889
+ break;
890
+ }
891
+ }
892
+ [inputStream close];
893
+
894
+ NSMutableData *postamble = [NSMutableData data];
895
+ [postamble appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
896
+ [postamble appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
897
+ [fileHandle writeData:postamble];
898
+ [fileHandle closeFile];
899
+
900
+ NSURL *tempFileURL = [NSURL fileURLWithPath:tempFilePath];
901
+
902
+ // Use delegate-based session for upload progress support with fromFile: instead of fromData:
903
+ NSURLSessionUploadTask *task = [self.fgSession uploadTaskWithRequest:request fromFile:tempFileURL];
858
904
  NSString *taskKey = [NSString stringWithFormat:@"%lu", (unsigned long)task.taskIdentifier];
859
905
 
860
906
  self.uploadTaskIdMap[taskKey] = uploadId;
861
- self.uploadPromises[uploadId] = @{@"resolve": resolve, @"reject": reject};
907
+ self.uploadPromises[uploadId] = @{
908
+ @"resolve": resolve,
909
+ @"reject": reject,
910
+ @"tempFile": tempFilePath
911
+ };
862
912
  self.uploadUrls[uploadId] = urlString;
863
913
 
864
914
  [task resume];
@@ -1113,9 +1163,10 @@ RCT_EXPORT_METHOD(unzip:(NSString *)sourcePath
1113
1163
  // pure-Foundation approach using NSInputStream with a known zip local-file header parser.
1114
1164
 
1115
1165
  // ── Pure-Foundation zip reader (no third-party, no subprocess) ──────────
1116
- NSData *zipData = [NSData dataWithContentsOfFile:sourcePath];
1166
+ NSError *readError = nil;
1167
+ NSData *zipData = [NSData dataWithContentsOfFile:sourcePath options:NSDataReadingMappedIfSafe error:&readError];
1117
1168
  if (!zipData) {
1118
- resolve(@{@"success": @NO, @"error": @"Cannot read zip file"});
1169
+ resolve(@{@"success": @NO, @"error": readError.localizedDescription ?: @"Cannot read zip file"});
1119
1170
  return;
1120
1171
  }
1121
1172
 
@@ -1300,7 +1351,14 @@ RCT_EXPORT_METHOD(zip:(NSString *)sourcePath
1300
1351
  // Delete existing destination file
1301
1352
  [fm removeItemAtPath:destPath error:nil];
1302
1353
 
1303
- NSMutableData *zipData = [NSMutableData new];
1354
+ // Create the destination file and open a handle for writing
1355
+ [fm createFileAtPath:destPath contents:nil attributes:nil];
1356
+ NSFileHandle *fh = [NSFileHandle fileHandleForWritingAtPath:destPath];
1357
+ if (!fh) {
1358
+ resolve(@{@"success": @NO, @"error": @"Failed to create destination zip file"});
1359
+ return;
1360
+ }
1361
+
1304
1362
  NSMutableArray<NSDictionary *> *centralDirectory = [NSMutableArray new];
1305
1363
 
1306
1364
  NSArray<NSString *> *filesToZip;
@@ -1310,9 +1368,6 @@ RCT_EXPORT_METHOD(zip:(NSString *)sourcePath
1310
1368
  NSMutableArray *files = [NSMutableArray new];
1311
1369
  NSString *file;
1312
1370
  while ((file = [enumerator nextObject])) {
1313
- NSString *fullPath = [sourcePath stringByAppendingPathComponent:file];
1314
- BOOL entryIsDir = NO;
1315
- [fm fileExistsAtPath:fullPath isDirectory:&entryIsDir];
1316
1371
  [files addObject:file];
1317
1372
  }
1318
1373
  filesToZip = files;
@@ -1327,80 +1382,138 @@ RCT_EXPORT_METHOD(zip:(NSString *)sourcePath
1327
1382
  BOOL entryIsDir = NO;
1328
1383
  [fm fileExistsAtPath:fullPath isDirectory:&entryIsDir];
1329
1384
 
1330
- NSData *fileData = entryIsDir ? [NSData data] : [NSData dataWithContentsOfFile:fullPath];
1331
- if (!fileData) continue;
1332
-
1333
1385
  NSData *entryNameData = [relativePath dataUsingEncoding:NSUTF8StringEncoding];
1334
1386
  uint16_t nameLen = (uint16_t)entryNameData.length;
1335
1387
 
1336
- // Deflate compress (skip compression for directories)
1337
- NSData *compressedData;
1338
- uint16_t method;
1388
+ uint32_t localHeaderOffset = (uint32_t)[fh offsetInFile];
1389
+
1390
+ // Write local file header with placeholder CRC/sizes
1391
+ uint32_t sig = CFSwapInt32HostToLittle(0x04034b50);
1392
+ uint16_t version = CFSwapInt16HostToLittle(20);
1393
+ uint16_t flags = 0;
1394
+ uint16_t modTime = 0, modDate = 0;
1395
+ uint16_t extraLen = 0;
1396
+
1397
+ // Placeholders — will be patched after streaming
1398
+ uint16_t method = 0;
1399
+ uint32_t crcLE = 0;
1400
+ uint32_t compSz = 0;
1401
+ uint32_t uncompSz = 0;
1402
+
1403
+ [fh writeData:[NSData dataWithBytes:&sig length:4]];
1404
+ [fh writeData:[NSData dataWithBytes:&version length:2]];
1405
+ [fh writeData:[NSData dataWithBytes:&flags length:2]];
1406
+ [fh writeData:[NSData dataWithBytes:&method length:2]]; // offset +8
1407
+ [fh writeData:[NSData dataWithBytes:&modTime length:2]];
1408
+ [fh writeData:[NSData dataWithBytes:&modDate length:2]];
1409
+ [fh writeData:[NSData dataWithBytes:&crcLE length:4]]; // offset +14
1410
+ [fh writeData:[NSData dataWithBytes:&compSz length:4]]; // offset +18
1411
+ [fh writeData:[NSData dataWithBytes:&uncompSz length:4]]; // offset +22
1412
+ [fh writeData:[NSData dataWithBytes:&nameLen length:2]];
1413
+ [fh writeData:[NSData dataWithBytes:&extraLen length:2]];
1414
+ [fh writeData:entryNameData];
1415
+
1339
1416
  uint32_t crc = 0;
1417
+ uint32_t compressedSize = 0;
1418
+ uint32_t uncompressedSize = 0;
1419
+ uint16_t compressionMethod = 0;
1340
1420
 
1341
- if (entryIsDir || fileData.length == 0) {
1342
- compressedData = fileData;
1343
- method = 0;
1421
+ if (entryIsDir) {
1422
+ // Directory entry — no data to write
1344
1423
  } else {
1345
- // zlib deflate (raw, -15)
1346
- uLongf bound = compressBound((uLong)fileData.length);
1347
- NSMutableData *comp = [NSMutableData dataWithLength:bound];
1424
+ NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:fullPath];
1425
+ [inputStream open];
1426
+
1427
+ if (!inputStream || inputStream.streamStatus == NSStreamStatusError) {
1428
+ [inputStream close];
1429
+ continue;
1430
+ }
1431
+
1432
+ // Stream-compress with zlib deflate
1348
1433
  z_stream strm;
1349
1434
  memset(&strm, 0, sizeof(strm));
1350
- strm.next_in = (Bytef *)fileData.bytes;
1351
- strm.avail_in = (uInt)fileData.length;
1352
- strm.next_out = (Bytef *)comp.mutableBytes;
1353
- strm.avail_out = (uInt)bound;
1354
1435
  deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
1355
- deflate(&strm, Z_FINISH);
1436
+
1437
+ uint8_t inBuf[32768];
1438
+ uint8_t outBuf[32768];
1439
+ int flush = Z_NO_FLUSH;
1440
+
1441
+ while ([inputStream hasBytesAvailable]) {
1442
+ NSInteger bytesRead = [inputStream read:inBuf maxLength:sizeof(inBuf)];
1443
+ if (bytesRead <= 0) break;
1444
+
1445
+ crc = (uint32_t)crc32((uLong)crc, inBuf, (uInt)bytesRead);
1446
+ uncompressedSize += (uint32_t)bytesRead;
1447
+
1448
+ strm.next_in = inBuf;
1449
+ strm.avail_in = (uInt)bytesRead;
1450
+
1451
+ if (![inputStream hasBytesAvailable]) {
1452
+ flush = Z_FINISH;
1453
+ }
1454
+
1455
+ do {
1456
+ strm.next_out = outBuf;
1457
+ strm.avail_out = sizeof(outBuf);
1458
+ deflate(&strm, flush);
1459
+ NSUInteger produced = sizeof(outBuf) - strm.avail_out;
1460
+ if (produced > 0) {
1461
+ [fh writeData:[NSData dataWithBytes:outBuf length:produced]];
1462
+ compressedSize += (uint32_t)produced;
1463
+ }
1464
+ } while (strm.avail_out == 0);
1465
+ }
1466
+
1467
+ // Finalize deflate if we didn't hit Z_FINISH yet
1468
+ if (flush != Z_FINISH) {
1469
+ strm.next_in = NULL;
1470
+ strm.avail_in = 0;
1471
+ do {
1472
+ strm.next_out = outBuf;
1473
+ strm.avail_out = sizeof(outBuf);
1474
+ deflate(&strm, Z_FINISH);
1475
+ NSUInteger produced = sizeof(outBuf) - strm.avail_out;
1476
+ if (produced > 0) {
1477
+ [fh writeData:[NSData dataWithBytes:outBuf length:produced]];
1478
+ compressedSize += (uint32_t)produced;
1479
+ }
1480
+ } while (strm.avail_out == 0);
1481
+ }
1482
+
1356
1483
  deflateEnd(&strm);
1357
- [comp setLength:strm.total_out];
1358
- compressedData = comp;
1359
- method = 8;
1484
+ [inputStream close];
1485
+ compressionMethod = 8;
1360
1486
  }
1361
1487
 
1362
- // CRC32
1363
- crc = (uint32_t)crc32(0L, (const Bytef *)fileData.bytes, (uInt)fileData.length);
1488
+ // Patch the local file header with actual CRC, sizes, method
1489
+ unsigned long long currentPos = [fh offsetInFile];
1490
+ uint16_t methLE = CFSwapInt16HostToLittle(compressionMethod);
1491
+ uint32_t crcPatch = CFSwapInt32HostToLittle(crc);
1492
+ uint32_t csPatch = CFSwapInt32HostToLittle(compressedSize);
1493
+ uint32_t usPatch = CFSwapInt32HostToLittle(uncompressedSize);
1364
1494
 
1365
- uint32_t localHeaderOffset = (uint32_t)zipData.length;
1495
+ [fh seekToFileOffset:localHeaderOffset + 8];
1496
+ [fh writeData:[NSData dataWithBytes:&methLE length:2]];
1497
+ [fh seekToFileOffset:localHeaderOffset + 10]; // skip modTime
1498
+ [fh seekToFileOffset:localHeaderOffset + 14];
1499
+ [fh writeData:[NSData dataWithBytes:&crcPatch length:4]];
1500
+ [fh writeData:[NSData dataWithBytes:&csPatch length:4]];
1501
+ [fh writeData:[NSData dataWithBytes:&usPatch length:4]];
1366
1502
 
1367
- // Local file header
1368
- uint32_t sig = CFSwapInt32HostToLittle(0x04034b50);
1369
- uint16_t version = CFSwapInt16HostToLittle(20);
1370
- uint16_t flags = 0;
1371
- uint16_t meth = CFSwapInt16HostToLittle(method);
1372
- uint16_t modTime = 0, modDate = 0; // no time for simplicity
1373
- uint32_t crcLE = CFSwapInt32HostToLittle(crc);
1374
- uint32_t compSz = CFSwapInt32HostToLittle((uint32_t)compressedData.length);
1375
- uint32_t uncompSz = CFSwapInt32HostToLittle((uint32_t)fileData.length);
1376
- uint16_t extraLen = 0;
1377
-
1378
- [zipData appendBytes:&sig length:4];
1379
- [zipData appendBytes:&version length:2];
1380
- [zipData appendBytes:&flags length:2];
1381
- [zipData appendBytes:&meth length:2];
1382
- [zipData appendBytes:&modTime length:2];
1383
- [zipData appendBytes:&modDate length:2];
1384
- [zipData appendBytes:&crcLE length:4];
1385
- [zipData appendBytes:&compSz length:4];
1386
- [zipData appendBytes:&uncompSz length:4];
1387
- [zipData appendBytes:&nameLen length:2];
1388
- [zipData appendBytes:&extraLen length:2];
1389
- [zipData appendData:entryNameData];
1390
- [zipData appendData:compressedData];
1503
+ [fh seekToFileOffset:currentPos]; // restore position
1391
1504
 
1392
1505
  [centralDirectory addObject:@{
1393
1506
  @"name": entryNameData,
1394
- @"method": @(method),
1507
+ @"method": @(compressionMethod),
1395
1508
  @"crc": @(crc),
1396
- @"compSize": @(compressedData.length),
1397
- @"uncompSize": @(fileData.length),
1509
+ @"compSize": @(compressedSize),
1510
+ @"uncompSize": @(uncompressedSize),
1398
1511
  @"localOffset": @(localHeaderOffset),
1399
1512
  }];
1400
1513
  }
1401
1514
 
1402
1515
  // Central directory
1403
- uint32_t cdOffset = (uint32_t)zipData.length;
1516
+ uint32_t cdOffset = (uint32_t)[fh offsetInFile];
1404
1517
  for (NSDictionary *entry in centralDirectory) {
1405
1518
  NSData *nameData = entry[@"name"];
1406
1519
  uint16_t nameLen = (uint16_t)nameData.length;
@@ -1419,27 +1532,27 @@ RCT_EXPORT_METHOD(zip:(NSString *)sourcePath
1419
1532
  uint32_t extAttr = 0;
1420
1533
  uint32_t localOff = CFSwapInt32HostToLittle((uint32_t)[entry[@"localOffset"] unsignedIntValue]);
1421
1534
 
1422
- [zipData appendBytes:&cdSig length:4];
1423
- [zipData appendBytes:&verMade length:2];
1424
- [zipData appendBytes:&verNeeded length:2];
1425
- [zipData appendBytes:&flags length:2];
1426
- [zipData appendBytes:&meth length:2];
1427
- [zipData appendBytes:&modTime length:2];
1428
- [zipData appendBytes:&modDate length:2];
1429
- [zipData appendBytes:&crcLE length:4];
1430
- [zipData appendBytes:&compSz length:4];
1431
- [zipData appendBytes:&uncompSz length:4];
1432
- [zipData appendBytes:&nameLen length:2];
1433
- [zipData appendBytes:&extraLen length:2];
1434
- [zipData appendBytes:&commentLen length:2];
1435
- [zipData appendBytes:&disk length:2];
1436
- [zipData appendBytes:&intAttr length:2];
1437
- [zipData appendBytes:&extAttr length:4];
1438
- [zipData appendBytes:&localOff length:4];
1439
- [zipData appendData:nameData];
1535
+ [fh writeData:[NSData dataWithBytes:&cdSig length:4]];
1536
+ [fh writeData:[NSData dataWithBytes:&verMade length:2]];
1537
+ [fh writeData:[NSData dataWithBytes:&verNeeded length:2]];
1538
+ [fh writeData:[NSData dataWithBytes:&flags length:2]];
1539
+ [fh writeData:[NSData dataWithBytes:&meth length:2]];
1540
+ [fh writeData:[NSData dataWithBytes:&modTime length:2]];
1541
+ [fh writeData:[NSData dataWithBytes:&modDate length:2]];
1542
+ [fh writeData:[NSData dataWithBytes:&crcLE length:4]];
1543
+ [fh writeData:[NSData dataWithBytes:&compSz length:4]];
1544
+ [fh writeData:[NSData dataWithBytes:&uncompSz length:4]];
1545
+ [fh writeData:[NSData dataWithBytes:&nameLen length:2]];
1546
+ [fh writeData:[NSData dataWithBytes:&extraLen length:2]];
1547
+ [fh writeData:[NSData dataWithBytes:&commentLen length:2]];
1548
+ [fh writeData:[NSData dataWithBytes:&disk length:2]];
1549
+ [fh writeData:[NSData dataWithBytes:&intAttr length:2]];
1550
+ [fh writeData:[NSData dataWithBytes:&extAttr length:4]];
1551
+ [fh writeData:[NSData dataWithBytes:&localOff length:4]];
1552
+ [fh writeData:nameData];
1440
1553
  }
1441
1554
 
1442
- uint32_t cdSize = CFSwapInt32HostToLittle((uint32_t)(zipData.length - cdOffset));
1555
+ uint32_t cdSize = CFSwapInt32HostToLittle((uint32_t)([fh offsetInFile] - cdOffset));
1443
1556
  uint32_t cdOffLE = CFSwapInt32HostToLittle(cdOffset);
1444
1557
  uint16_t numEntries = CFSwapInt16HostToLittle((uint16_t)centralDirectory.count);
1445
1558
  uint16_t commentLen = 0;
@@ -1447,21 +1560,17 @@ RCT_EXPORT_METHOD(zip:(NSString *)sourcePath
1447
1560
  // End of central directory record
1448
1561
  uint32_t eocdSig = CFSwapInt32HostToLittle(0x06054b50);
1449
1562
  uint16_t disk = 0;
1450
- [zipData appendBytes:&eocdSig length:4];
1451
- [zipData appendBytes:&disk length:2];
1452
- [zipData appendBytes:&disk length:2];
1453
- [zipData appendBytes:&numEntries length:2];
1454
- [zipData appendBytes:&numEntries length:2];
1455
- [zipData appendBytes:&cdSize length:4];
1456
- [zipData appendBytes:&cdOffLE length:4];
1457
- [zipData appendBytes:&commentLen length:2];
1458
-
1459
- NSError *writeErr = nil;
1460
- if ([zipData writeToFile:destPath options:NSDataWritingAtomic error:&writeErr]) {
1461
- resolve(@{@"success": @YES, @"zipPath": destPath});
1462
- } else {
1463
- resolve(@{@"success": @NO, @"error": writeErr.localizedDescription ?: @"ZIP_WRITE_ERROR"});
1464
- }
1563
+ [fh writeData:[NSData dataWithBytes:&eocdSig length:4]];
1564
+ [fh writeData:[NSData dataWithBytes:&disk length:2]];
1565
+ [fh writeData:[NSData dataWithBytes:&disk length:2]];
1566
+ [fh writeData:[NSData dataWithBytes:&numEntries length:2]];
1567
+ [fh writeData:[NSData dataWithBytes:&numEntries length:2]];
1568
+ [fh writeData:[NSData dataWithBytes:&cdSize length:4]];
1569
+ [fh writeData:[NSData dataWithBytes:&cdOffLE length:4]];
1570
+ [fh writeData:[NSData dataWithBytes:&commentLen length:2]];
1571
+
1572
+ [fh closeFile];
1573
+ resolve(@{@"success": @YES, @"zipPath": destPath});
1465
1574
  });
1466
1575
  }
1467
1576