diff --git a/.flowconfig b/.flowconfig index 7d0709c9..47e911ad 100644 --- a/.flowconfig +++ b/.flowconfig @@ -70,4 +70,4 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError [version] -0.78.0 +0.109.0 diff --git a/.gitignore b/.gitignore index 0322b55c..ad5bfac1 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ android/*.iml android/local.properties android/.settings android/.project +Session.vim diff --git a/CHANGELOG.md b/CHANGELOG.md index e9367758..f1f6c234 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,43 @@ # Changelog +## Changes for v2.20 + +- #1037 Invalidate upload session + +## Changes vor v2.19 + +- #1048 fix: use correct type for stat size #1048 +- #1063 RNFSManager:readDir iOS crash fix #1063 +- #1036 fix: addListener and removeListeners methods wass added to pass warning with Native Event Emitter #1036 +- RNFSManager iOS crash in readDir +- fix: use correct type for stat size +- make react-native-windows peer dependency optional #1016 + +## Changes for v2.17 + +- #938 Manually flush & invalidate session upon completion +- #954 Switch to using channels for byte copying. +- #962 Size limit with copyAssetsVideoIOS +- #963 Add basic support for Android content URIs in stat +- #972 Manually flush & invalidate completed session +- #974 Add content for copyAssetsFileIOS method +- #975 Fix copyAssetsFileIOS's image resizing option to resize to exact width and height +- #985 README.md: clarify usage of readFileRes +- #986 macOS support + +## Changes for v2.16 +- #797 Added path to the Android download folder +- #842 Fixes #811 Concurrent downloads send progress updates to all progress callbacks +- #783 Import RCTImageLoaderProtocol instead of RCTImageLoader to fix iOS build on RN>0.60 + +## Changes for v2.15 +- #717 bugfix #443: Add option progressInterval for downloadFile +- #759 Fix for issue #749: RNFS.uploadFiles upload raw not multipart +- #728 Correctly read binaryStreamOnly param +- #752 Fix Xcode and Java deprecation warnings +- #779 Add conditional comments around methods not supported in Mac Catalyst +- #736 Added support for ph:// uris to copyAssetsFileIOS + ## Changes for v2.14 - #718 Add tvOS deployment target to podspec - #710 Added existsRes function to Android diff --git a/Downloader.h b/Downloader.h index 9c0a6031..82042523 100644 --- a/Downloader.h +++ b/Downloader.h @@ -4,7 +4,7 @@ typedef void (^DownloadCompleteCallback)(NSNumber*, NSNumber*); typedef void (^ErrorCallback)(NSError*); typedef void (^BeginCallback)(NSNumber*, NSNumber*, NSDictionary*); typedef void (^ProgressCallback)(NSNumber*, NSNumber*); -typedef void (^ResumableCallback)(); +typedef void (^ResumableCallback)(void); @interface RNFSDownloadParams : NSObject @@ -19,8 +19,10 @@ typedef void (^ResumableCallback)(); @property bool background; // Whether to continue download when app is in background @property bool discretionary; // Whether the file may be downloaded at the OS's discretion (iOS only) @property bool cacheable; // Whether the file may be stored in the shared NSURLCache (iOS only) +@property (copy) NSNumber* progressInterval; @property (copy) NSNumber* progressDivider; -@property (copy) NSNumber* readTimeout; +@property (copy) NSNumber* readTimeout; // How long (in milliseconds) a task should wait for additional data to arrive before giving up +@property (copy) NSNumber* backgroundTimeout; // How long (in milliseconds) to wait for an entire resource to transfer before giving up @end diff --git a/Downloader.m b/Downloader.m index 91ba9883..cb2ca238 100644 --- a/Downloader.m +++ b/Downloader.m @@ -11,6 +11,7 @@ @interface RNFSDownloader() @property (retain) NSURLSession* session; @property (retain) NSURLSessionDownloadTask* task; @property (retain) NSNumber* statusCode; +@property (assign) NSTimeInterval lastProgressEmitTimestamp; @property (retain) NSNumber* lastProgressValue; @property (retain) NSNumber* contentLength; @property (retain) NSNumber* bytesWritten; @@ -25,9 +26,10 @@ @implementation RNFSDownloader - (NSString *)downloadFile:(RNFSDownloadParams*)params { NSString *uuid = nil; - + _params = params; + _lastProgressEmitTimestamp = 0; _bytesWritten = 0; NSURL* url = [NSURL URLWithString:_params.fromUrl]; @@ -61,27 +63,34 @@ - (NSString *)downloadFile:(RNFSDownloadParams*)params config.HTTPAdditionalHeaders = _params.headers; config.timeoutIntervalForRequest = [_params.readTimeout intValue] / 1000.0; + config.timeoutIntervalForResource = [_params.backgroundTimeout intValue] / 1000.0; _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil]; _task = [_session downloadTaskWithURL:url]; [_task resume]; - + return uuid; } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)downloadTask.response; - if (!_statusCode) { + if (_params.beginCallback && !_statusCode) { _statusCode = [NSNumber numberWithLong:httpResponse.statusCode]; _contentLength = [NSNumber numberWithLong:httpResponse.expectedContentLength]; return _params.beginCallback(_statusCode, _contentLength, httpResponse.allHeaderFields); } - if ([_statusCode isEqualToNumber:[NSNumber numberWithInt:200]]) { + if (_params.progressCallback && [_statusCode isEqualToNumber:[NSNumber numberWithInt:200]]) { _bytesWritten = @(totalBytesWritten); - if (_params.progressDivider.integerValue <= 0) { + if(_params.progressInterval.integerValue > 0){ + NSTimeInterval timestamp = [[NSDate date] timeIntervalSince1970]; + if(timestamp - _lastProgressEmitTimestamp > _params.progressInterval.integerValue / 1000.0){ + _lastProgressEmitTimestamp = timestamp; + return _params.progressCallback(_contentLength, _bytesWritten); + } + }else if (_params.progressDivider.integerValue <= 0) { return _params.progressCallback(_contentLength, _bytesWritten); } else { double doubleBytesWritten = (double)[_bytesWritten longValue]; @@ -90,7 +99,7 @@ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTas NSNumber* progress = [NSNumber numberWithUnsignedInt: floor(doublePercents)]; if ([progress unsignedIntValue] % [_params.progressDivider integerValue] == 0) { if (([progress unsignedIntValue] != [_lastProgressValue unsignedIntValue]) || ([_bytesWritten unsignedIntegerValue] == [_contentLength longValue])) { - NSLog(@"---Progress callback EMIT--- %zu", [progress unsignedIntValue]); + NSLog(@"---Progress callback EMIT--- %u", [progress unsignedIntValue]); _lastProgressValue = [NSNumber numberWithUnsignedInt:[progress unsignedIntValue]]; return _params.progressCallback(_contentLength, _bytesWritten); } @@ -119,18 +128,31 @@ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTas NSLog(@"RNFS download: unable to move tempfile to destination. %@, %@", error, error.userInfo); } + // When numerous downloads are called the sessions are not always invalidated and cleared by iOS14. + // This leads to error 28 – no space left on device so we manually flush and invalidate to free up space + if(session != nil){ + [session flushWithCompletionHandler:^{ + [session finishTasksAndInvalidate]; + }]; + } + return _params.completeCallback(_statusCode, _bytesWritten); } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { - if (error && error.code != NSURLErrorCancelled) { + if (error) { + NSLog(@"RNFS download: didCompleteWithError %@, %@", error, error.userInfo); + if (error.code != NSURLErrorCancelled) { _resumeData = error.userInfo[NSURLSessionDownloadTaskResumeData]; if (_resumeData != nil) { - _params.resumableCallback(); + if (_params.resumableCallback) { + _params.resumableCallback(); + } } else { _params.errorCallback(error); } + } } } @@ -140,15 +162,17 @@ - (void)stopDownload [_task cancelByProducingResumeData:^(NSData * _Nullable resumeData) { if (resumeData != nil) { self.resumeData = resumeData; - _params.resumableCallback(); + if (self->_params.resumableCallback) { + self->_params.resumableCallback(); + } } else { NSError *error = [NSError errorWithDomain:@"RNFS" - code:@"Aborted" + code:0 //used to pass an NSString @"Aborted" here, but it needs an NSInteger userInfo:@{ NSLocalizedDescriptionKey: @"Download has been aborted" }]; - - _params.errorCallback(error); + + self->_params.errorCallback(error); } }]; diff --git a/Examples/.npmignore b/Examples/.npmignore new file mode 100644 index 00000000..7718eb3a --- /dev/null +++ b/Examples/.npmignore @@ -0,0 +1,2 @@ +# Make sure we don't publish examples +RNFS.Windows/ diff --git a/Examples/RNFS.Windows/.buckconfig b/Examples/RNFS.Windows/.buckconfig new file mode 100644 index 00000000..934256cb --- /dev/null +++ b/Examples/RNFS.Windows/.buckconfig @@ -0,0 +1,6 @@ + +[android] + target = Google Inc.:Google APIs:23 + +[maven_repositories] + central = https://repo1.maven.org/maven2 diff --git a/Examples/RNFS.Windows/.eslintrc.js b/Examples/RNFS.Windows/.eslintrc.js new file mode 100644 index 00000000..18969972 --- /dev/null +++ b/Examples/RNFS.Windows/.eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + root: true, + extends: '@react-native-community', + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint'], +}; diff --git a/Examples/RNFS.Windows/.gitattributes b/Examples/RNFS.Windows/.gitattributes new file mode 100644 index 00000000..d42ff183 --- /dev/null +++ b/Examples/RNFS.Windows/.gitattributes @@ -0,0 +1 @@ +*.pbxproj -text diff --git a/Examples/RNFS.Windows/.gitignore b/Examples/RNFS.Windows/.gitignore new file mode 100644 index 00000000..4deaf9ea --- /dev/null +++ b/Examples/RNFS.Windows/.gitignore @@ -0,0 +1,65 @@ +yarn.lock + +# OSX +# +.DS_Store + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate + +# Android/IntelliJ +# +build/ +.idea +.gradle +local.properties +*.iml + +# Visual Studio Code +# +.vscode/ + +# node.js +# +node_modules/ +npm-debug.log +yarn-error.log + +# BUCK +buck-out/ +\.buckd/ +*.keystore +!debug.keystore + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/ + +*/fastlane/report.xml +*/fastlane/Preview.html +*/fastlane/screenshots + +# Bundle artifact +*.jsbundle + +# CocoaPods +/ios/Pods/ diff --git a/Examples/RNFS.Windows/.prettierrc.js b/Examples/RNFS.Windows/.prettierrc.js new file mode 100644 index 00000000..5c4de1a4 --- /dev/null +++ b/Examples/RNFS.Windows/.prettierrc.js @@ -0,0 +1,6 @@ +module.exports = { + bracketSpacing: false, + jsxBracketSameLine: true, + singleQuote: true, + trailingComma: 'all', +}; diff --git a/Examples/RNFS.Windows/.watchmanconfig b/Examples/RNFS.Windows/.watchmanconfig new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/Examples/RNFS.Windows/.watchmanconfig @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/Examples/RNFS.Windows/App.tsx b/Examples/RNFS.Windows/App.tsx new file mode 100644 index 00000000..c13d11d9 --- /dev/null +++ b/Examples/RNFS.Windows/App.tsx @@ -0,0 +1,967 @@ +/** + * Sample React Native App + * https://github.com/facebook/react-native + * + * Generated with the TypeScript template + * https://github.com/react-native-community/react-native-template-typescript + * + * @format + */ + +import React, { useState } from 'react'; +import { + SafeAreaView, + StyleSheet, + ScrollView, + View, + Text, + StatusBar, + TextInput, + Button, + Alert, + Picker, +} from 'react-native'; + + +import RNFS from 'react-native-fs'; +//import {Picker} from '@react-native-community/picker'; + +import { + Colors, + DebugInstructions, + ReloadInstructions, +} from 'react-native/Libraries/NewAppScreen'; + + +const App: () => React$Node = () => { + + const [mkdirParam, setMkdirParam] = useState(''); + + const [moveFileSource, setMoveFileSource] = useState(''); + const [moveFileDest, setMoveFileDest] = useState(''); + + const [copyFileSource, setCopyFileSource] = useState(''); + const [copyFileDest, setCopyFileDest] = useState(''); + + const [unlinkFileParam, setUnlinkFileParam] = useState(''); + + const [readDirParam, setReadDirParam] = useState(''); + + const [statParam, setStatParam] = useState(''); + + const [readFileParam, setReadFileParam] = useState(''); + + const [readParam, setReadParam] = useState(''); + const [readLengthParam, setReadLengthParam] = useState(''); + const [readPositionParam, setReadPositionParam] = useState(''); + + const [hashFileParam, setHashFileParam] = useState(''); + const [selectedValue, setSelectedValue] = useState('md5'); + + const [writeFileParam, setWriteFileParam] = useState(''); + const [writeFileContentValue, setWriteFileContentValue] = useState(''); + + const [appendFileParam, setAppendFileParam] = useState(''); + const [appendContentValue, setAppendContentValue] = useState(''); + + const [writeParam, setWriteParam] = useState(''); + const [writeContentValue, setWriteContentValue] = useState(''); + const [writePositionValue, setWritePositionValue] = useState(''); + + const [touchFilePathParam, setTouchFilePathParam] = useState(''); + + const [downloadFilePathParam, setDownloadFilePathParam] = useState(''); + const [downloadFileSource, setDownloadFileSource] = useState(''); + const [downloadFileName, setDownloadFileName] = useState(''); + + const [uploadFileSource1, setUploadFileSource1] = useState(''); + const [uploadFileSource2, setUploadFileSource2] = useState(''); + const [uploadFileDestination, setUploadFileDestination] = useState(''); + + const [existsSource, setExistsSource] = useState(''); + + const mkdirExample = () => { + if(mkdirParam.length > 0) { + RNFS.mkdir(RNFS.DocumentDirectoryPath + '/' + mkdirParam) + .then((result) => { + Alert.alert('Successfully created directory.') + }) + .catch((err) => { + Alert.alert(err.message) + }) + } + } + + const moveFileExample = () => { + if(moveFileSource.length > 0) { + RNFS.moveFile(RNFS.DocumentDirectoryPath + '/' + moveFileSource, + RNFS.DocumentDirectoryPath + '/' + moveFileDest) + .then((result) => { + Alert.alert('Successfully moved file to specified destination.') + }) + .catch((err) => { + Alert.alert(err.message) + }) + } + } + + const copyFileExample = () => { + if(copyFileSource.length > 0) { + RNFS.copyFile(RNFS.DocumentDirectoryPath + '/' + copyFileSource, + RNFS.DocumentDirectoryPath + '/' + copyFileDest) + .then((result) => { + Alert.alert('Successfully put copy of file to specified destination.') + }) + .catch((err) => { + Alert.alert(err.message) + }) + } + } + + const copyFolderExample = () => { + if(copyFileSource.length > 0) { + RNFS.copyFolder(RNFS.DocumentDirectoryPath + '/' + copyFileSource, + RNFS.DocumentDirectoryPath + '/' + copyFileDest) + .then((result) => { + Alert.alert('Successfully put copy of file to specified destination.') + }) + .catch((err) => { + Alert.alert(err.message) + }) + } + } + + const getFSInfoExample = () => { + RNFS.getFSInfo() + .then((result) => { + Alert.alert('Total space: ' + result.totalSpace + ' bytes\nFree space: ' + result.freeSpace + ' bytes') + }) + .catch((err) => { + Alert.alert(err.message) + }) + } + + const unlinkExample = () => { + if(unlinkFileParam.length > 0) { + RNFS.unlink(RNFS.DocumentDirectoryPath + '/' + unlinkFileParam) + .then((result) => { + Alert.alert('Successfully unlinked specified file or folder') + }) + .catch((err) => { + Alert.alert(err.message) + }) + } + } + + const readDirExample = () => { + RNFS.readDir(RNFS.DocumentDirectoryPath + '/' + readDirParam) + .then((result) => { + if(result.length == 0) { + Alert.alert('Directory is empty') + } + else { + let title = 'Number of contents: ' + result.length + let output = '\nresult[0].name: ' + result[0].name + + '\nresult[0].size: ' + result[0].size + ' bytes' + + '\nresult[0].mtime: ' + result[0].mtime + + '\nresult[0].ctime: ' + result[0].ctime + + '\nresult[0].name: ' + result[0].name + + '\nresult[0].path: ' + result[0].path + + '\nresult[0].isFile(): ' + result[0].isFile() + + '\nresult[0].isDirectory(): ' + result[0].isDirectory() + Alert.alert(title, output) + } + }) + .catch((err) => { + Alert.alert(err.message) + }) + } + + const statExample = () => { + RNFS.stat(RNFS.DocumentDirectoryPath + '/' + statParam) + .then((result) => { + let title = 'Stat Results:' + let output = 'result.path: ' + result.path + + '\nresult.ctime: ' + result.ctime + + '\nresult.mtime: ' + result.mtime + + '\nresult.size: ' + result.size + ' bytes' + + '\nresult.mode: ' + result.mode + + '\nresult.originalFilePath: ' + result.originalFilePath + + '\nresult.isFile(): ' + result.isFile() + + '\nresult.isDirectory(): ' + result.isDirectory() + Alert.alert(title, output) + }) + .catch((err) => { + Alert.alert(err.message) + }) + } + + const readFileExample = () => { + if(readFileParam.length > 0) { + RNFS.readFile(RNFS.DocumentDirectoryPath + '/' + readFileParam) + .then((result) => { + Alert.alert('File Contents:', result.substr(0,50)) + }) + .catch((err) => { + Alert.alert(err.message) + }) + } + } + + const readExample = () => { + if(readParam.length > 0) { + + var length = parseInt(readLengthParam, 10) + var position = parseInt(readPositionParam, 10) + + if(length == NaN || position == NaN) { + Alert.alert('Length and Position must be integers') + return + } + + RNFS.read(RNFS.DocumentDirectoryPath + '/' + readParam, length, position) + .then((result) => { + Alert.alert('File Contents:', result) + }) + .catch((err) => { + Alert.alert(err.message) + }) + } + } + + const hashFileExample = () => { + if(hashFileParam.length > 0) { + RNFS.hash(RNFS.DocumentDirectoryPath + '/' + hashFileParam, selectedValue) + .then((result) => { + Alert.alert('Hashed File Contents:', result) + }) + .catch((err) => { + Alert.alert(err.message) + }) + } + } + + const writeFileExample = () => { + + if(writeFileParam.length > 0 && writeFileContentValue.length > 0) { + RNFS.writeFile(RNFS.DocumentDirectoryPath + '/' + writeFileParam, writeFileContentValue) + .then((result) => { + Alert.alert('Successfully Wrote to File') + }) + .catch((err) => { + Alert.alert(err.message) + }) + } + } + + const appendFileExample = () => { + + if(appendFileParam.length > 0 && appendContentValue.length > 0) { + RNFS.appendFile(RNFS.DocumentDirectoryPath + '/' + appendFileParam, appendContentValue) + .then((result) => { + Alert.alert('Successfully Appended to File') + }) + .catch((err) => { + Alert.alert(err.message) + }) + } + } + + const writeExample = () => { + + if(writeParam.length > 0 && writeContentValue.length > 0) { + + var position = parseInt(writePositionValue, 10) + + if(position == NaN) { + Alert.alert('Length and Position must be integers') + return + } + + RNFS.write(RNFS.DocumentDirectoryPath + '/' + writeParam, writeContentValue, position) + .then((result) => { + Alert.alert('Successfully Wrote to File ') + }) + .catch((err) => { + Alert.alert(err.message) + }) + } + } + + const touchFileExample = () => { + RNFS.touch(RNFS.DocumentDirectoryPath + '/' + touchFilePathParam, new Date('December 17, 1988 03:24:00'), new Date('1989-12-17T03:24:00')) + .then((result) => { + Alert.alert('Successfully Appended to File') + }) + .catch((err) => { + Alert.alert(err.message) + }) + } + + const downloadFileExample = () => { + RNFS.downloadFile({ + fromUrl: downloadFileSource, + toFile: RNFS.DocumentDirectoryPath + '/' + downloadFilePathParam +'/' + downloadFileName, + begin: () => {console.log('It has begun!');}, + progress: () => {console.log('It is going!');}, + progressDivider: 7, + }).promise.then((r) => { + console.log('Successfully Downloaded File', r.jobId + ' ' + r.statusCode + ' ' + r.bytesWritten); + }) + .catch((err) => { + console.log(err.message); + }); + + } + + const uploadFileExample = () => { + if(uploadFileSource1.length > 0 && uploadFileSource2.length > 0) { + //var uploadUrl = uploadFileDestination; // For testing purposes, go to http://requestb.in/ and create your own link + // create an array of objects of the files you want to upload + var files = [ + { + name: 'test1', + filename: 'test1.png', + filepath: RNFS.DocumentDirectoryPath + '/' + uploadFileSource1, + }, { + name: 'test2', + filename: 'test2.png', + filepath: RNFS.DocumentDirectoryPath + '/' + uploadFileSource2, + } + ]; + + var uploadBegin = (response) => { + var jobId = response.jobId; + console.log('UPLOAD HAS BEGUN! JobId: ' + jobId); + }; + + var uploadProgress = (response) => { + var percentage = Math.floor((response.totalBytesSent/response.totalBytesExpectedToSend) * 100); + console.log('UPLOAD IS ' + percentage + '% DONE!'); + }; + + // upload files + RNFS.uploadFiles({ + toUrl: uploadFileDestination, + files: files, + method: 'POST', + headers: { + 'authorization': 'myOwnToken', + 'content-language': 'en-US', + }, + fields: { + 'asdasdadfhdvbf': 'asdg', + 'asd': 'asdgasfdghasd', + }, + begin: uploadBegin, + progress: uploadProgress, + }).promise.then((response) => { + if (response.statusCode == 200) { + console.log('FILES UPLOADED!'); // response.statusCode, response.headers, response.body + } else { + console.log('SERVER ERROR'); + } + }) + .catch((err) => { + if(err.description === "cancelled") { + console.log('User cancelled'); + } + console.log(err); + }); + + } + } + + const uploadFileExample2 = () => { + if(uploadFileSource1.length > 0 && uploadFileSource2.length > 0) { + //var uploadUrl = uploadFileDestination; // For testing purposes, go to http://requestb.in/ and create your own link + // create an array of objects of the files you want to upload + var files = [ + { + name: 'test1', + filename: 'test1.png', + filepath: RNFS.DocumentDirectoryPath + '/' + uploadFileSource1, + }, { + name: 'test2', + filename: 'test2.png', + filepath: RNFS.DocumentDirectoryPath + '/' + uploadFileSource2, + } + ]; + + var uploadBegin = (response) => { + var jobId = response.jobId; + console.log('UPLOAD HAS BEGUN! JobId: ' + jobId); + }; + + var uploadProgress = (response) => { + var percentage = Math.floor((response.totalBytesSent/response.totalBytesExpectedToSend) * 100); + console.log('UPLOAD IS ' + percentage + '% DONE!'); + }; + + // upload files + RNFS.uploadFiles({ + toUrl: uploadFileDestination, + files: files, + method: 'POST', + headers: { + 'referer': 'toast', + }, + begin: uploadBegin, + progress: uploadProgress, + }).promise.then((response) => { + if (response.statusCode == 200) { + console.log('FILES UPLOADED!'); // response.statusCode, response.headers, response.body + } else { + console.log('SERVER ERROR'); + } + }) + .catch((err) => { + if(err.description === "cancelled") { + console.log('User cancelled'); + } + console.log(err); + }); + + } + } + + + const existsExample = () => { + RNFS.exists(RNFS.DocumentDirectoryPath + '/' + existsSource) + .then((result) => { + Alert.alert('Exists: ' + result) + }) + .catch((err) => { + Alert.alert(err.message) + }) + } + + return ( + <> + + + + {global.HermesInternal == null ? null : ( + + Engine: Hermes + + )} + + {"React Native File System Windows Demo App"} + + + + + + + {"RNFS.MainBundlePath: " + RNFS.MainBundlePath + '\n'} + {"RNFS.CachesDirectoryPath: " + RNFS.CachesDirectoryPath + '\n'} + {"RNFS.ExternalCachesDirectoryPath: " + RNFS.ExternalCachesDirectoryPath + '\n'} + {"RNFS.DocumentDirectoryPath: " + RNFS.DocumentDirectoryPath + '\n'} + {"RNFS.DownloadDirectoryPath: " + RNFS.DownloadDirectoryPath + '\n'} + {"RNFS.ExternalDirectoryPath: " + RNFS.ExternalDirectoryPath + '\n'} + {"RNFS.ExternalStorageDirectoryPath: " + RNFS.ExternalStorageDirectoryPath + '\n'} + {"RNFS.TemporaryDirectoryPath: " + RNFS.TemporaryDirectoryPath + '\n'} + {"RNFS.LibraryDirectoryPath: " + RNFS.LibraryDirectoryPath + '\n'} + {"RNFS.PicturesDirectoryPath: " + RNFS.PicturesDirectoryPath + '\n'} + {"RNFS.FileProtectionKeys: " + RNFS.FileProtectionKeys + '\n'} + + + + + + + + + {"mkdir"} + + + setMkdirParam(mkdirParam)} + placeholderTextColor = "#9a73ef" + autoCapitalize = "none" + /> + +