├── jsconfig.json
├── android
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── rnfm
│ │ ├── DownloadResult.java
│ │ ├── DownloadParams.java
│ │ ├── RNFileManagerPackage.java
│ │ ├── Downloader.java
│ │ └── RNFileManager.java
├── local.properties
├── build.gradle
├── gradlew.bat
└── gradlew
├── NSArray+Map.h
├── RNFileManager.h
├── .gitignore
├── NSArray+Map.m
├── Downloader.h
├── LICENSE
├── Downloader.m
├── index.js
├── package.json
├── README.md
├── RNFileManger.xcodeproj
└── project.pbxproj
└── RNFileManager.m
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES6",
4 | "module": "commonjs"
5 | }
6 | }
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thethreeonee/react-native-file-manager/HEAD/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/android/src/main/java/com/rnfm/DownloadResult.java:
--------------------------------------------------------------------------------
1 | package com.rnfm;
2 |
3 | public class DownloadResult {
4 | public int statusCode;
5 | public int bytesWritten;
6 | public Exception exception;
7 | }
8 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Oct 21 11:34:03 PDT 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
7 |
--------------------------------------------------------------------------------
/NSArray+Map.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSArray+Map.h
3 | // RNFS
4 | //
5 | // taken from http://stackoverflow.com/questions/6127638/nsarray-equivalent-of-map
6 |
7 | #import
8 |
9 | @interface NSArray (Map)
10 |
11 | - (NSArray *)rnfs_mapObjectsUsingBlock:(id (^)(id obj, NSUInteger idx))block;
12 |
13 | @end
--------------------------------------------------------------------------------
/RNFileManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // RNFSManager.h
3 | // RNFSManager
4 | //
5 | // Created by Johannes Lumpe on 08/05/15.
6 | // Copyright (c) 2015 Johannes Lumpe. All rights reserved.
7 | //
8 |
9 | #import "RCTBridgeModule.h"
10 | #import "RCTLog.h"
11 |
12 | @interface RNFileManager : NSObject
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | workbench
3 | *.log
4 | # Xcode
5 | .DS_Store
6 | build/
7 | *.pbxuser
8 | !default.pbxuser
9 | *.mode1v3
10 | !default.mode1v3
11 | *.mode2v3
12 | !default.mode2v3
13 | *.perspectivev3
14 | !default.perspectivev3
15 | *.xcworkspace
16 | !default.xcworkspace
17 | xcuserdata
18 | profile
19 | *.moved-aside
20 | DerivedData
21 | .idea/
22 | # Pods - for those of you who use CocoaPods
23 | Pods
24 | update-test.sh
25 | # AndroidStudio
26 | *.iml
27 |
--------------------------------------------------------------------------------
/NSArray+Map.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSArray+Map.m
3 | // RNFS
4 |
5 | #import "NSArray+Map.h"
6 |
7 | @implementation NSArray (Map)
8 |
9 | - (NSArray *)rnfs_mapObjectsUsingBlock:(id (^)(id obj, NSUInteger idx))block
10 | {
11 | NSMutableArray *result = [NSMutableArray arrayWithCapacity:[self count]];
12 |
13 | [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
14 | [result addObject:block(obj, idx)];
15 | }];
16 |
17 | return result;
18 | }
19 |
20 | @end
--------------------------------------------------------------------------------
/android/local.properties:
--------------------------------------------------------------------------------
1 | ## This file is automatically generated by Android Studio.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must *NOT* be checked into Version Control Systems,
5 | # as it contains information specific to your local configuration.
6 | #
7 | # Location of the SDK. This is only used by Gradle.
8 | # For customization when using a Version Control System, please read the
9 | # header note.
10 | #Tue Dec 08 10:57:21 CST 2015
11 | sdk.dir=/Users/MiincGu/Library/Android/sdk
12 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | jcenter()
4 | }
5 |
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:1.1.3'
8 | }
9 | }
10 |
11 | apply plugin: 'com.android.library'
12 |
13 | android {
14 | compileSdkVersion 23
15 | buildToolsVersion "23.0.1"
16 |
17 | defaultConfig {
18 | minSdkVersion 16
19 | targetSdkVersion 22
20 | versionCode 1
21 | versionName "1.0"
22 | }
23 | lintOptions {
24 | abortOnError false
25 | }
26 | }
27 |
28 | repositories {
29 | mavenCentral()
30 | }
31 |
32 | dependencies {
33 | compile 'com.facebook.react:react-native:0.12.+'
34 | }
35 |
--------------------------------------------------------------------------------
/android/src/main/java/com/rnfm/DownloadParams.java:
--------------------------------------------------------------------------------
1 | package com.rnfm;
2 |
3 | import java.io.File;
4 | import java.net.URL;
5 | import java.util.*;
6 |
7 | public class DownloadParams {
8 | public interface OnTaskCompleted {
9 | void onTaskCompleted(DownloadResult res);
10 | }
11 |
12 | public interface OnDownloadBegin {
13 | void onDownloadBegin(int statusCode, int contentLength, Map headers);
14 | }
15 |
16 | public interface OnDownloadProgress {
17 | void onDownloadProgress(int contentLength, int bytesWritten);
18 | }
19 |
20 | public URL src;
21 | public File dest;
22 | public OnTaskCompleted onTaskCompleted;
23 | public OnDownloadBegin onDownloadBegin;
24 | public OnDownloadProgress onDownloadProgress;
25 | }
--------------------------------------------------------------------------------
/Downloader.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | typedef void (^DownloaderCallback)(NSNumber*, NSNumber*);
4 | typedef void (^ErrorCallback)(NSError*);
5 | typedef void (^BeginCallback)(NSNumber*, NSNumber*, NSDictionary*);
6 | typedef void (^ProgressCallback)(NSNumber*, NSNumber*);
7 |
8 | @interface DownloadParams : NSObject
9 |
10 | @property (copy) NSString* fromUrl;
11 | @property (copy) NSString* toFile;
12 | @property (copy) DownloaderCallback callback; // Download has finished (data written)
13 | @property (copy) ErrorCallback errorCallback; // Something went wrong
14 | @property (copy) BeginCallback beginCallback; // Download has started (headers received)
15 | @property (copy) ProgressCallback progressCallback; // Download is progressing
16 |
17 | @end
18 |
19 | @interface Downloader : NSObject
20 |
21 | - (void)downloadFile:(DownloadParams*)params;
22 | - (void)stopDownload;
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/android/src/main/java/com/rnfm/RNFileManagerPackage.java:
--------------------------------------------------------------------------------
1 | package com.rnfm;
2 |
3 | import java.util.*;
4 |
5 | import com.facebook.react.ReactPackage;
6 | import com.facebook.react.bridge.NativeModule;
7 | import com.facebook.react.bridge.JavaScriptModule;
8 | import com.facebook.react.bridge.ReactApplicationContext;
9 | import com.facebook.react.uimanager.ViewManager;
10 |
11 | public class RNFileManagerPackage implements ReactPackage {
12 |
13 | @Override
14 | public List createNativeModules(ReactApplicationContext reactContext) {
15 | List modules = new ArrayList<>();
16 | modules.add(new RNFileManager(reactContext));
17 | return modules;
18 | }
19 |
20 | @Override
21 | public List> createJSModules() {
22 | return Collections.emptyList();
23 | }
24 |
25 | @Override
26 | public List createViewManagers(ReactApplicationContext reactContext) {
27 | return Arrays.asList();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Johannes Lumpe
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/android/src/main/java/com/rnfm/Downloader.java:
--------------------------------------------------------------------------------
1 | package com.rnfm;
2 |
3 | import java.io.File;
4 | import java.io.FileOutputStream;
5 | import java.io.FileInputStream;
6 | import java.io.BufferedInputStream;
7 | import java.io.InputStream;
8 | import java.io.OutputStream;
9 | import java.io.IOException;
10 | import java.net.URL;
11 | import java.net.URLConnection;
12 | import java.net.HttpURLConnection;
13 | import java.util.*;
14 | import java.util.concurrent.atomic.AtomicBoolean;
15 |
16 | import android.os.AsyncTask;
17 |
18 | public class Downloader extends AsyncTask {
19 | private DownloadParams mParam;
20 | private AtomicBoolean mAbort = new AtomicBoolean(false);
21 |
22 | protected DownloadResult doInBackground(DownloadParams... params) {
23 | mParam = params[0];
24 |
25 | DownloadResult res = new DownloadResult();
26 |
27 | try {
28 | this.download(mParam, res);
29 | mParam.onTaskCompleted.onTaskCompleted(res);
30 | } catch (Exception ex) {
31 | res.exception = ex;
32 | mParam.onTaskCompleted.onTaskCompleted(res);
33 | return res;
34 | }
35 |
36 | return res;
37 | }
38 |
39 | private void download(DownloadParams param, DownloadResult res) throws IOException {
40 | InputStream input = null;
41 | OutputStream output = null;
42 |
43 | try {
44 | HttpURLConnection connection = (HttpURLConnection)param.src.openConnection();
45 |
46 | connection.setConnectTimeout(5000);
47 | connection.connect();
48 |
49 | int statusCode = connection.getResponseCode();
50 | int lengthOfFile = connection.getContentLength();
51 |
52 | Map> headers = connection.getHeaderFields();
53 |
54 | Map headersFlat = new HashMap();
55 |
56 | for (Map.Entry> entry : headers.entrySet()) {
57 | String headerKey = entry.getKey();
58 | String valueKey = entry.getValue().get(0);
59 |
60 | if (headerKey != null && valueKey != null) {
61 | headersFlat.put(headerKey, valueKey);
62 | }
63 | }
64 |
65 | mParam.onDownloadBegin.onDownloadBegin(statusCode, lengthOfFile, headersFlat);
66 |
67 | input = new BufferedInputStream(param.src.openStream(), 8 * 1024);
68 | output = new FileOutputStream(param.dest);
69 |
70 | byte data[] = new byte[8 * 1024];
71 | int total = 0;
72 | int count;
73 |
74 | while ((count = input.read(data)) != -1) {
75 | if (mAbort.get()) {
76 | break;
77 | }
78 |
79 | total += count;
80 | publishProgress(new int[] { lengthOfFile, total });
81 | output.write(data, 0, count);
82 | }
83 |
84 | output.flush();
85 |
86 | res.statusCode = statusCode;
87 | res.bytesWritten = total;
88 | } finally {
89 | if (output != null) output.close();
90 | if (input != null) input.close();
91 | }
92 | }
93 |
94 | protected void stop() {
95 | mAbort.set(true);
96 | }
97 |
98 | @Override
99 | protected void onProgressUpdate(int[]... values) {
100 | super.onProgressUpdate(values);
101 | mParam.onDownloadProgress.onDownloadProgress(values[0][0], values[0][1]);
102 | }
103 |
104 | protected void onPostExecute(Exception ex) {
105 |
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/Downloader.m:
--------------------------------------------------------------------------------
1 | #import "Downloader.h"
2 |
3 | @implementation DownloadParams
4 |
5 | @end
6 |
7 | @interface Downloader()
8 |
9 | @property (copy) DownloadParams* params;
10 |
11 | @property (retain) NSURLConnection* connection;
12 | @property (retain) NSNumber* statusCode;
13 | @property (retain) NSNumber* contentLength;
14 | @property (retain) NSNumber* bytesWritten;
15 |
16 | @property (retain) NSFileHandle* fileHandle;
17 |
18 | @end
19 |
20 | @implementation Downloader
21 |
22 | - (void)downloadFile:(DownloadParams*)params
23 | {
24 | _params = params;
25 |
26 | _bytesWritten = 0;
27 |
28 | NSURL* url = [NSURL URLWithString:_params.fromUrl];
29 |
30 | NSMutableURLRequest* downloadRequest = [NSMutableURLRequest requestWithURL:url
31 | cachePolicy:NSURLRequestUseProtocolCachePolicy
32 | timeoutInterval:30];
33 |
34 | _connection = [[NSURLConnection alloc] initWithRequest:downloadRequest delegate:self startImmediately:NO];
35 |
36 | [_connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
37 |
38 | [_connection start];
39 | }
40 |
41 | - (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error
42 | {
43 | [_fileHandle closeFile];
44 |
45 | NSString *tempPath = [_params.toFile stringByAppendingPathExtension:@"tmp"];
46 |
47 | NSError *err = nil;
48 | if ([[NSFileManager defaultManager] fileExistsAtPath:tempPath isDirectory:false]) {
49 | [[NSFileManager defaultManager] removeItemAtPath:tempPath error:&err];
50 | }
51 |
52 | return _params.errorCallback(error);
53 | }
54 |
55 | - (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
56 | {
57 | NSString *tempPath = [_params.toFile stringByAppendingPathExtension:@"tmp"];
58 |
59 | [[NSFileManager defaultManager] createFileAtPath:tempPath contents:nil attributes:nil];
60 | [[NSURL fileURLWithPath:tempPath] setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
61 |
62 | _fileHandle = [NSFileHandle fileHandleForWritingAtPath:tempPath];
63 |
64 | if (!_fileHandle) {
65 | NSError* error = [NSError errorWithDomain:@"Downloader" code:NSURLErrorFileDoesNotExist userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat: @"Failed to create target file at path: %@", tempPath]}];
66 |
67 | if (_params.errorCallback) {
68 | return _params.errorCallback(error);
69 | }
70 | }
71 |
72 | NSHTTPURLResponse* httpUrlResponse = (NSHTTPURLResponse*)response;
73 |
74 | _statusCode = [NSNumber numberWithLong:httpUrlResponse.statusCode];
75 | _contentLength = [NSNumber numberWithLongLong: httpUrlResponse.expectedContentLength];
76 |
77 | return _params.beginCallback(_statusCode, _contentLength, httpUrlResponse.allHeaderFields);
78 | }
79 |
80 | - (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
81 | {
82 | if ([_statusCode isEqualToNumber:[NSNumber numberWithInt:200]]) {
83 | [_fileHandle writeData:data];
84 |
85 | _bytesWritten = [NSNumber numberWithUnsignedInteger:[_bytesWritten unsignedIntegerValue] + data.length];
86 |
87 | return _params.progressCallback(_contentLength, _bytesWritten);
88 | }
89 | }
90 |
91 | - (void)connectionDidFinishLoading:(NSURLConnection*)connection
92 | {
93 | [_fileHandle closeFile];
94 |
95 | NSString *tempPath = [_params.toFile stringByAppendingPathExtension:@"tmp"];
96 |
97 | NSError *error = nil;
98 | if ([[NSFileManager defaultManager] fileExistsAtPath:_params.toFile isDirectory:false]) {
99 | [[NSFileManager defaultManager] removeItemAtPath:_params.toFile error:&error];
100 | }
101 | [[NSFileManager defaultManager] moveItemAtPath:tempPath toPath:_params.toFile error:&error];
102 | [[NSURL fileURLWithPath:_params.toFile] setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
103 |
104 | NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:_params.toFile error:&error];
105 |
106 | return _params.callback(_statusCode, [fileAttributes valueForKey:@"NSFileSize"]);
107 | }
108 |
109 | - (void)stopDownload
110 | {
111 | [_connection cancel];
112 | }
113 |
114 | @end
115 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This file supports both iOS and Android
4 |
5 | // Stop bluebird going nuts because it can't find "self"
6 | if (typeof self === 'undefined') {
7 | global.self = global;
8 | }
9 |
10 | var RNFSManager = require('react-native').NativeModules.RNFileManager;
11 | var NativeAppEventEmitter = require('react-native').NativeAppEventEmitter; // iOS
12 | var DeviceEventEmitter = require('react-native').DeviceEventEmitter; // Android
13 | var Promise = require('bluebird');
14 | var base64 = require('base-64');
15 | var utf8 = require('utf8');
16 |
17 | var _readDir = Promise.promisify(RNFSManager.readDir);
18 | var _stat = Promise.promisify(RNFSManager.stat);
19 | var _readFile = Promise.promisify(RNFSManager.readFile);
20 | var _writeFile = Promise.promisify(RNFSManager.writeFile);
21 | var _unlink = Promise.promisify(RNFSManager.unlink);
22 | var _mkdir = Promise.promisify(RNFSManager.mkdir);
23 | var _downloadFile = Promise.promisify(RNFSManager.downloadFile);
24 | var _pathForBundle = Promise.promisify(RNFSManager.pathForBundle);
25 | var _exists = Promise.promisify(RNFSManager.exists);
26 | var _folderExists = Promise.promisify(RNFSManager.folderExists);
27 | var _rename = Promise.promisify(RNFSManager.rename);
28 | var _moveFile = Promise.promisify(RNFSManager.moveFile);
29 |
30 | var convertError = (err) => {
31 | if (err.isOperational && err.cause) {
32 | err = err.cause;
33 | }
34 |
35 | var error = new Error(err.description || err.message);
36 | error.code = err.code;
37 | throw error;
38 | };
39 |
40 | var NSFileTypeRegular = RNFSManager.NSFileTypeRegular;
41 | var NSFileTypeDirectory = RNFSManager.NSFileTypeDirectory;
42 |
43 | var jobId = 0;
44 |
45 | var getJobId = () => {
46 | jobId += 1;
47 | return jobId;
48 | };
49 |
50 | var RNFS = {
51 |
52 | readDir(dirpath) {
53 | return _readDir(dirpath)
54 | .then(files => {
55 | return files.map(file => ({
56 | name: file.name,
57 | path: file.path,
58 | size: file.size,
59 | isFile: () => file.type === NSFileTypeRegular,
60 | isDirectory: () => file.type === NSFileTypeDirectory,
61 | }));
62 | })
63 | .catch(convertError);
64 | },
65 |
66 | // Node style version (lowercase d). Returns just the names
67 | readdir(dirpath) {
68 | return RNFS.readDir(dirpath)
69 | .then(files => {
70 | return files.map(file => file.name);
71 | });
72 | },
73 |
74 | stat(filepath) {
75 | return _stat(filepath)
76 | .then((result) => {
77 | return {
78 | 'ctime': new Date(result.ctime*1000),
79 | 'mtime': new Date(result.mtime*1000),
80 | 'size': result.size,
81 | 'mode': result.mode,
82 | isFile: () => result.type === NSFileTypeRegular,
83 | isDirectory: () => result.type === NSFileTypeDirectory,
84 | };
85 | })
86 | .catch(convertError);
87 | },
88 |
89 | readFile(filepath, encoding) {
90 | if (!encoding) encoding = 'utf8';
91 |
92 | return _readFile(filepath)
93 | .then((b64) => {
94 | var contents;
95 |
96 | if (encoding === 'utf8') {
97 | contents = utf8.decode(base64.decode(b64));
98 | } else if (encoding === 'ascii') {
99 | contents = base64.decode(b64);
100 | } else if (encoding === 'base64') {
101 | contents = b64;
102 | } else {
103 | throw new Error('Invalid encoding type "' + encoding + '"');
104 | }
105 |
106 | return contents;
107 | })
108 | .catch(convertError);
109 | },
110 |
111 | writeFile(filepath, contents, encoding, options) {
112 | var b64;
113 |
114 | if (!encoding) encoding = 'utf8';
115 |
116 | if (encoding === 'utf8') {
117 | b64 = base64.encode(utf8.encode(contents));
118 | } else if (encoding === 'ascii') {
119 | b64 = base64.encode(contents);
120 | } else if (encoding === 'base64') {
121 | b64 = contents;
122 | } else {
123 | throw new Error('Invalid encoding type "' + encoding + '"');
124 | }
125 |
126 | return _writeFile(filepath, b64, options)
127 | .catch(convertError);
128 | },
129 |
130 | pathForBundle(bundleName) {
131 | return _pathForBundle(bundleName);
132 | },
133 |
134 | unlink(filepath) {
135 | return _unlink(filepath)
136 | .catch(convertError);
137 | },
138 |
139 | mkdir(filepath, excludeFromBackup) {
140 | excludeFromBackup = !!excludeFromBackup;
141 | return _mkdir(filepath, excludeFromBackup)
142 | .catch(convertError);
143 | },
144 |
145 | downloadFile(fromUrl, toFile, begin, progress) {
146 | var jobId = getJobId();
147 | var subscriptionIos, subscriptionAndroid;
148 |
149 | if (!begin) begin = (info) => {
150 | // console.log('Download begun:', info);
151 | };
152 |
153 | if (begin) {
154 | // Two different styles of subscribing to events for different platforms, hmmm....
155 | if (NativeAppEventEmitter.addListener)
156 | subscriptionIos = NativeAppEventEmitter.addListener('DownloadBegin-' + jobId, begin);
157 | if (DeviceEventEmitter.addListener)
158 | subscriptionAndroid = DeviceEventEmitter.addListener('DownloadBegin-' + jobId, begin);
159 | }
160 |
161 | if (progress) {
162 | if (NativeAppEventEmitter.addListener)
163 | subscriptionIos = NativeAppEventEmitter.addListener('DownloadProgress-' + jobId, progress);
164 | if (DeviceEventEmitter.addListener)
165 | subscriptionAndroid = DeviceEventEmitter.addListener('DownloadProgress-' + jobId, progress);
166 | }
167 |
168 | return _downloadFile(fromUrl, toFile, jobId)
169 | .then(res => {
170 | if (subscriptionIos) subscriptionIos.remove();
171 | if (subscriptionAndroid) subscriptionAndroid.remove();
172 | return res;
173 | })
174 | .catch(convertError);
175 | },
176 |
177 | stopDownload(jobId) {
178 | RNFSManager.stopDownload(jobId);
179 | },
180 |
181 | fileExistsAtPath(filepath) {
182 | return _exists(filepath)
183 | .catch(convertError);
184 | },
185 |
186 | folderExistsAtPath(path) {
187 | return _folderExists(path)
188 | .catch(convertError);
189 | },
190 |
191 | renameFile(filepath, newName) {
192 | return _rename(filepath, newName)
193 | .catch(convertError);
194 | },
195 |
196 | moveFile(filepath, newPath) {
197 | return _moveFile(filepath, newPath)
198 | .catch(convertError);
199 | },
200 |
201 | MainBundlePath: RNFSManager.MainBundlePath,
202 | CachesDirectoryPath: RNFSManager.NSCachesDirectoryPath,
203 | DocumentDirectoryPath: RNFSManager.NSDocumentDirectoryPath,
204 | ApplicationSupportDirectoryPath: RNFSManager.NSApplicationSupportDirectoryPath
205 | };
206 |
207 | module.exports = RNFS;
208 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-file-manager",
3 | "version": "1.0.1",
4 | "description": "A file manger for react native",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://MiincGu@github.com/MiincGu/react-native-file-manager.git"
12 | },
13 | "keywords": [
14 | "react-native"
15 | ],
16 | "author": {
17 | "name": "MiincGu"
18 | },
19 | "license": "ISC",
20 | "bugs": {
21 | "url": "https://github.com/MiincGu/react-native-file-manager/issues"
22 | },
23 | "homepage": "https://github.com/MiincGu/react-native-file-manager#readme",
24 | "dependencies": {
25 | "base-64": "^0.1.0",
26 | "bluebird": "^2.9.25",
27 | "utf8": "^2.1.1"
28 | },
29 | "gitHead": "2714088d4daf0c72a7971c306dd01afcfacd1a27",
30 | "readme": "## react-native-file-manager\n\nNative file system manager for react-native\n\n## Usage (iOS)\n\nFirst you need to install react-native-file-manager:\n\n```javascript\nnpm install react-native-file-manager --save\n```\n\nIn XCode, in the project navigator, right click Libraries ➜ Add Files to [your project's name] Go to node_modules ➜ react-native-file-manager and add the .xcodeproj file\n\nIn XCode, in the project navigator, select your project. Add the lib*.a from the RNFileManager project to your project's Build Phases ➜ Link Binary With Libraries Click .xcodeproj file you added before in the project navigator and go the Build Settings tab. Make sure 'All' is toggled on (instead of 'Basic'). Look for Header Search Paths and make sure it contains both $(SRCROOT)/../react-native/React and $(SRCROOT)/../../React - mark both as recursive.\n\nRun your project (Cmd+R)\n\n## Usage (Android)\n\nAndroid support is currently limited to only the `DocumentDirectory`. This maps to the app's `files` directory.\n\nMake alterations to the following files:\n\n* `android/settings.gradle`\n\n```gradle\n...\ninclude ':react-native-file-manager'\nproject(':react-native-file-manager').projectDir = new File(settingsDir, '../node_modules/react-native-file-manager/android')\n```\n\n* `android/app/build.gradle`\n\n```gradle\n...\ndependencies {\n ...\n compile project(':react-native-file-manager')\n}\n```\n\n* register module (in MainActivity.java)\n\n```java\nimport com.rnfm.RNFileManagerPackage; // <--- import\n\npublic class MainActivity extends Activity implements DefaultHardwareBackBtnHandler {\n\n ......\n\n @Override\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n mReactRootView = new ReactRootView(this);\n\n mReactInstanceManager = ReactInstanceManager.builder()\n .setApplication(getApplication())\n .setBundleAssetName(\"index.android.bundle\")\n .setJSMainModuleName(\"index.android\")\n .addPackage(new MainReactPackage())\n .addPackage(new RNFileMangerPackage()) // <------- add package\n .setUseDeveloperSupport(BuildConfig.DEBUG)\n .setInitialLifecycleState(LifecycleState.RESUMED)\n .build();\n\n mReactRootView.startReactApplication(mReactInstanceManager, \"ExampleRN\", null);\n\n setContentView(mReactRootView);\n }\n\n ......\n\n}\n```\n\n## Examples\n\n### Basic\n\n```javascript\n// require the module\nvar RNFileManager = require('react-native-file-manager');\n\n// get a list of files and directories in the main bundle\nRNFileManager.readDir(RNFileManager.MainBundlePath)\n .then((result) => {\n console.log('GOT RESULT', result);\n\n // stat the first file\n return Promise.all([RNFileManager.stat(result[0].path), result[0].path]);\n })\n .then((statResult) => {\n if (statResult[0].isFile()) {\n // if we have a file, read it\n return RNFileManager.readFile(statResult[1], 'utf8');\n }\n\n return 'no file';\n })\n .then((contents) => {\n // log the file contents\n console.log(contents);\n })\n .catch((err) => {\n console.log(err.message, err.code);\n });\n```\n\n### File creation\n\n```javascript\n// require the module\nvar RNFileManager = require('react-native-file-manager');\n\n// create a path you want to write to\nvar path = RNFileManager.DocumentDirectoryPath + '/test.txt';\n\n// write the file\nRNFileManager.writeFile(path, 'Lorem ipsum dolor sit amet', 'utf8')\n .then((success) => {\n console.log('FILE WRITTEN!');\n })\n .catch((err) => {\n console.log(err.message);\n });\n\n```\n\n### File deletion\n```javascript\n// create a path you want to delete\nvar path = RNFileManager.DocumentDirectoryPath + '/test.txt';\n\nreturn RNFileManager.unlink(path)\n // spread is a method offered by bluebird to allow for more than a\n // single return value of a promise. If you use `then`, you will receive\n // the values inside of an array\n .spread((success, path) => {\n console.log('FILE DELETED', success, path);\n })\n // `unlink` will throw an error, if the item to unlink does not exist\n .catch((err) => {\n console.log(err.message);\n });\n```\n\n## API\n\n### Constants\n\nThe following constants are available on the `RNFileManager` export:\n\n`MainBundlePath` (`String`) The absolute path to the main bundle directory \n`CachesDirectoryPath` (`String`) The absolute path to the caches directory \n`DocumentDirectoryPath` (`String`) The absolute path to the document directory\n\n### `promise readDir(path)`\n\nReads the contents of `path`. This must be an absolute path. Use the above path constants to form a usable file path.\n\nThe returned promise resolves with an array of objects with the following properties:\n\n`name` (`String`) - The name of the item \n`path` (`String`) - The absolute path to the item \n`size` (`Number`) - Size in bytes\n\n### `promise readdir(path)`\n\nNode.js style version of `readDir` that returns only the names. Note the lowercase `d`.\n\n### `promise stat(path)`\n\nStats an item at `path`. \nThe promise resolves with an object with the following properties:\n\n`ctime` (`Date`) - The creation date of the item \n`mtime` (`Date`) - The modification date of the item \n`size` (`Number`) - The size of the item in bytes \n`isFile` (`Function`) - Returns true when the item is a file \n`isDirectory` (`Function`) - Returns true when the item is a directory\n\n### `promise readFile(path [, encoding])`\n\nReads the file at `path` and return contents. `encoding` can be one of `utf8` (default), `ascii`, `base64`. Use `base64` for reading binary files.\n\nNote: you will take quite a performance hit if you are reading big files\n\n### `promise writeFile(filepath, contents [, encoding, options])`\n\nWrite the `contents` to `filepath`. `encoding` can be one of `utf8` (default), `ascii`, `base64`. `options` optionally takes an object specifying the file's properties, like mode etc.\n\nThe promise resolves with a boolean.\n\n### `promise unlink(filepath)`\n\nUnlinks the item at `filepath`. If the item does not exist, an error will be thrown.\n\nThe promise resolves with an array, which contains a boolean and the path that has been unlinked. Tip: use `spread` to receive the two arguments instead of a single array in your handler.\n\nAlso recursively deletes directories (works like Linux `rm -rf`).\n\n### `promise mkdir(filepath [, excludeFromBackup])`\n\nCreate a directory at `filepath`. Automatically creates parents and does not throw if already exists (works like Linux `mkdir -p`).\n\nIOS only: If `excludeFromBackup` is true, then `NSURLIsExcludedFromBackupKey` attribute will be set. Apple will *reject* apps for storing offline cache data that does not have this attribute.\n\n### `promise downloadFile(url, filepath [, beginCallback, progressCallback])`\n\nDownload file from `url` to `filepath`. Will overwrite any previously existing file.\n\nIf `beginCallback` is provided, it will be invoked once upon download starting when headers have been received and passed a single argument with the following properties:\n\n`jobId` (`Number`) - The download job ID, required if one wishes to cancel the download. See `stopDownload`. \n`statusCode` (`Number`) - The HTTP status code \n`contentLength` (`Number`) - The total size in bytes of the download resource \n`headers` (`Map`) - The HTTP response headers from the server \n\nIf `progressCallback` is provided, it will be invoked continuously and passed a single argument with the following properties:\n\n`contentLength` (`Number`) - The total size in bytes of the download resource \n`bytesWritten` (`Number`) - The number of bytes written to the file so far \n\nPercentage can be computed easily by dividing `bytesWritten` by `contentLength`.\n\n### `promise stopDownload(jobId)`\n\nAbort the current download job with this ID. The partial file will remain on the filesystem.\n",
31 | "readmeFilename": "README.md",
32 | "_id": "react-native-file-manager@1.0.1",
33 | "_shasum": "25b4a4b389680b022812d636463d70e3a815f47f",
34 | "_from": "miincgu/react-native-file-manager",
35 | "_resolved": "git://github.com/miincgu/react-native-file-manager.git#2714088d4daf0c72a7971c306dd01afcfacd1a27"
36 | }
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NOT MAINTAINED ANY MORE
2 |
3 | Change name from react-native-fs for personal use. Use on your own risk.
4 |
5 | ## react-native-file-manager
6 |
7 | Native file system manager for react-native
8 |
9 | ## Usage (iOS)
10 |
11 | First you need to install react-native-file-manager:
12 |
13 | ```javascript
14 | npm install react-native-file-manager --save
15 | ```
16 |
17 | In XCode, in the project navigator, right click Libraries ➜ Add Files to [your project's name] Go to node_modules ➜ react-native-file-manager and add the .xcodeproj file
18 |
19 | In XCode, in the project navigator, select your project. Add the lib*.a from the RNFileManager project to your project's Build Phases ➜ Link Binary With Libraries Click .xcodeproj file you added before in the project navigator and go the Build Settings tab. Make sure 'All' is toggled on (instead of 'Basic'). Look for Header Search Paths and make sure it contains both $(SRCROOT)/../react-native/React and $(SRCROOT)/../../React - mark both as recursive.
20 |
21 | Run your project (Cmd+R)
22 |
23 | ## Usage (Android)
24 |
25 | Android support is currently limited to only the `DocumentDirectory`. This maps to the app's `files` directory.
26 |
27 | Make alterations to the following files:
28 |
29 | * `android/settings.gradle`
30 |
31 | ```gradle
32 | ...
33 | include ':react-native-file-manager'
34 | project(':react-native-file-manager').projectDir = new File(settingsDir, '../node_modules/react-native-file-manager/android')
35 | ```
36 |
37 | * `android/app/build.gradle`
38 |
39 | ```gradle
40 | ...
41 | dependencies {
42 | ...
43 | compile project(':react-native-file-manager')
44 | }
45 | ```
46 |
47 | * register module (in MainActivity.java)
48 |
49 | ```java
50 | import com.rnfm.RNFileManagerPackage; // <--- import
51 |
52 | public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler {
53 |
54 | ......
55 |
56 | @Override
57 | protected void onCreate(Bundle savedInstanceState) {
58 | super.onCreate(savedInstanceState);
59 | mReactRootView = new ReactRootView(this);
60 |
61 | mReactInstanceManager = ReactInstanceManager.builder()
62 | .setApplication(getApplication())
63 | .setBundleAssetName("index.android.bundle")
64 | .setJSMainModuleName("index.android")
65 | .addPackage(new MainReactPackage())
66 | .addPackage(new RNFileMangerPackage()) // <------- add package
67 | .setUseDeveloperSupport(BuildConfig.DEBUG)
68 | .setInitialLifecycleState(LifecycleState.RESUMED)
69 | .build();
70 |
71 | mReactRootView.startReactApplication(mReactInstanceManager, "ExampleRN", null);
72 |
73 | setContentView(mReactRootView);
74 | }
75 |
76 | ......
77 |
78 | }
79 | ```
80 |
81 | ## Examples
82 |
83 | ### Basic
84 |
85 | ```javascript
86 | // require the module
87 | var RNFileManager = require('react-native-file-manager');
88 |
89 | // get a list of files and directories in the main bundle
90 | RNFileManager.readDir(RNFileManager.MainBundlePath)
91 | .then((result) => {
92 | console.log('GOT RESULT', result);
93 |
94 | // stat the first file
95 | return Promise.all([RNFileManager.stat(result[0].path), result[0].path]);
96 | })
97 | .then((statResult) => {
98 | if (statResult[0].isFile()) {
99 | // if we have a file, read it
100 | return RNFileManager.readFile(statResult[1], 'utf8');
101 | }
102 |
103 | return 'no file';
104 | })
105 | .then((contents) => {
106 | // log the file contents
107 | console.log(contents);
108 | })
109 | .catch((err) => {
110 | console.log(err.message, err.code);
111 | });
112 | ```
113 |
114 | ### File creation
115 |
116 | ```javascript
117 | // require the module
118 | var RNFileManager = require('react-native-file-manager');
119 |
120 | // create a path you want to write to
121 | var path = RNFileManager.DocumentDirectoryPath + '/test.txt';
122 |
123 | // write the file
124 | RNFileManager.writeFile(path, 'Lorem ipsum dolor sit amet', 'utf8')
125 | .then((success) => {
126 | console.log('FILE WRITTEN!');
127 | })
128 | .catch((err) => {
129 | console.log(err.message);
130 | });
131 |
132 | ```
133 |
134 | ### File deletion
135 | ```javascript
136 | // create a path you want to delete
137 | var path = RNFileManager.DocumentDirectoryPath + '/test.txt';
138 |
139 | return RNFileManager.unlink(path)
140 | // spread is a method offered by bluebird to allow for more than a
141 | // single return value of a promise. If you use `then`, you will receive
142 | // the values inside of an array
143 | .spread((success, path) => {
144 | console.log('FILE DELETED', success, path);
145 | })
146 | // `unlink` will throw an error, if the item to unlink does not exist
147 | .catch((err) => {
148 | console.log(err.message);
149 | });
150 | ```
151 |
152 | ## API
153 |
154 | ### Constants
155 |
156 | The following constants are available on the `RNFileManager` export:
157 |
158 | `MainBundlePath` (`String`) The absolute path to the main bundle directory
159 | `CachesDirectoryPath` (`String`) The absolute path to the caches directory
160 | `DocumentDirectoryPath` (`String`) The absolute path to the document directory
161 |
162 | ### `promise readDir(path)`
163 |
164 | Reads the contents of `path`. This must be an absolute path. Use the above path constants to form a usable file path.
165 |
166 | The returned promise resolves with an array of objects with the following properties:
167 |
168 | `name` (`String`) - The name of the item
169 | `path` (`String`) - The absolute path to the item
170 | `size` (`Number`) - Size in bytes
171 |
172 | ### `promise readdir(path)`
173 |
174 | Node.js style version of `readDir` that returns only the names. Note the lowercase `d`.
175 |
176 | ### `promise stat(path)`
177 |
178 | Stats an item at `path`.
179 | The promise resolves with an object with the following properties:
180 |
181 | `ctime` (`Date`) - The creation date of the item
182 | `mtime` (`Date`) - The modification date of the item
183 | `size` (`Number`) - The size of the item in bytes
184 | `isFile` (`Function`) - Returns true when the item is a file
185 | `isDirectory` (`Function`) - Returns true when the item is a directory
186 |
187 | ### `promise readFile(path [, encoding])`
188 |
189 | Reads the file at `path` and return contents. `encoding` can be one of `utf8` (default), `ascii`, `base64`. Use `base64` for reading binary files.
190 |
191 | Note: you will take quite a performance hit if you are reading big files
192 |
193 | ### `promise writeFile(filepath, contents [, encoding, options])`
194 |
195 | Write the `contents` to `filepath`. `encoding` can be one of `utf8` (default), `ascii`, `base64`. `options` optionally takes an object specifying the file's properties, like mode etc.
196 |
197 | The promise resolves with a boolean.
198 |
199 | ### `promise unlink(filepath)`
200 |
201 | Unlinks the item at `filepath`. If the item does not exist, an error will be thrown.
202 |
203 | The promise resolves with an array, which contains a boolean and the path that has been unlinked. Tip: use `spread` to receive the two arguments instead of a single array in your handler.
204 |
205 | Also recursively deletes directories (works like Linux `rm -rf`).
206 |
207 | ### `promise mkdir(filepath [, excludeFromBackup])`
208 |
209 | Create a directory at `filepath`. Automatically creates parents and does not throw if already exists (works like Linux `mkdir -p`).
210 |
211 | IOS only: If `excludeFromBackup` is true, then `NSURLIsExcludedFromBackupKey` attribute will be set. Apple will *reject* apps for storing offline cache data that does not have this attribute.
212 |
213 | ### `promise downloadFile(url, filepath [, beginCallback, progressCallback])`
214 |
215 | Download file from `url` to `filepath`. Will overwrite any previously existing file.
216 |
217 | If `beginCallback` is provided, it will be invoked once upon download starting when headers have been received and passed a single argument with the following properties:
218 |
219 | `jobId` (`Number`) - The download job ID, required if one wishes to cancel the download. See `stopDownload`.
220 | `statusCode` (`Number`) - The HTTP status code
221 | `contentLength` (`Number`) - The total size in bytes of the download resource
222 | `headers` (`Map`) - The HTTP response headers from the server
223 |
224 | If `progressCallback` is provided, it will be invoked continuously and passed a single argument with the following properties:
225 |
226 | `contentLength` (`Number`) - The total size in bytes of the download resource
227 | `bytesWritten` (`Number`) - The number of bytes written to the file so far
228 |
229 | Percentage can be computed easily by dividing `bytesWritten` by `contentLength`.
230 |
231 | ### `promise stopDownload(jobId)`
232 |
233 | Abort the current download job with this ID. The partial file will remain on the filesystem.
234 |
--------------------------------------------------------------------------------
/android/src/main/java/com/rnfm/RNFileManager.java:
--------------------------------------------------------------------------------
1 | package com.rnfm;
2 |
3 | import java.util.List;
4 | import java.util.Map;
5 | import java.util.HashMap;
6 | import java.util.ArrayList;
7 |
8 | import android.os.Environment;
9 | import android.os.AsyncTask;
10 | import android.util.Base64;
11 | import android.content.Context;
12 | import android.support.annotation.Nullable;
13 | import android.util.SparseArray;
14 |
15 | import java.io.File;
16 | import java.io.FileOutputStream;
17 | import java.io.FileInputStream;
18 | import java.io.BufferedInputStream;
19 | import java.io.InputStream;
20 | import java.io.OutputStream;
21 | import java.io.IOException;
22 | import java.net.URL;
23 | import java.net.URLConnection;
24 | import java.net.HttpURLConnection;
25 |
26 | import com.facebook.react.bridge.NativeModule;
27 | import com.facebook.react.bridge.ReactApplicationContext;
28 | import com.facebook.react.bridge.ReactContext;
29 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
30 | import com.facebook.react.bridge.ReactMethod;
31 | import com.facebook.react.bridge.Callback;
32 | import com.facebook.react.bridge.ReadableMap;
33 | import com.facebook.react.bridge.WritableMap;
34 | import com.facebook.react.bridge.Arguments;
35 | import com.facebook.react.bridge.WritableArray;
36 | import com.facebook.react.modules.core.DeviceEventManagerModule;
37 |
38 | public class RNFileManager extends ReactContextBaseJavaModule {
39 |
40 | private static final String NSDocumentDirectoryPath = "NSDocumentDirectoryPath";
41 | private static final String NSDocumentDirectory = "NSDocumentDirectory";
42 |
43 | private static final String NSFileTypeRegular = "NSFileTypeRegular";
44 | private static final String NSFileTypeDirectory = "NSFileTypeDirectory";
45 |
46 | private SparseArray downloaders = new SparseArray();
47 |
48 | public RNFileManager(ReactApplicationContext reactContext) {
49 | super(reactContext);
50 | }
51 |
52 | @Override
53 | public String getName() {
54 | return "RNFSManager";
55 | }
56 |
57 | @ReactMethod
58 | public void writeFile(String filepath, String base64Content, ReadableMap options, Callback callback) {
59 | try {
60 | byte[] bytes = Base64.decode(base64Content, Base64.DEFAULT);
61 |
62 | FileOutputStream outputStream = new FileOutputStream(filepath);
63 | outputStream.write(bytes);
64 | outputStream.close();
65 |
66 | callback.invoke(null, true, filepath);
67 | } catch (Exception ex) {
68 | ex.printStackTrace();
69 | callback.invoke(makeErrorPayload(ex));
70 | }
71 | }
72 |
73 | @ReactMethod
74 | public void readFile(String filepath, Callback callback) {
75 | try {
76 | File file = new File(filepath);
77 |
78 | if (!file.exists()) throw new Exception("File does not exist");
79 |
80 | FileInputStream inputStream = new FileInputStream(filepath);
81 | byte[] buffer = new byte[(int)file.length()];
82 | inputStream.read(buffer);
83 |
84 | String base64Content = Base64.encodeToString(buffer, Base64.NO_WRAP);
85 |
86 | callback.invoke(null, base64Content);
87 | } catch (Exception ex) {
88 | ex.printStackTrace();
89 | callback.invoke(makeErrorPayload(ex));
90 | }
91 | }
92 |
93 | @ReactMethod
94 | public void readDir(String directory, Callback callback) {
95 | try {
96 | File file = new File(directory);
97 |
98 | if (!file.exists()) throw new Exception("Folder does not exist");
99 |
100 | File[] files = file.listFiles();
101 |
102 | WritableArray fileMaps = Arguments.createArray();
103 |
104 | for (File childFile : files) {
105 | WritableMap fileMap = Arguments.createMap();
106 |
107 | fileMap.putString("name", childFile.getName());
108 | fileMap.putString("path", childFile.getAbsolutePath());
109 | fileMap.putInt("size", (int)childFile.length());
110 | fileMap.putInt("type", childFile.isDirectory() ? 1 : 0);
111 |
112 | fileMaps.pushMap(fileMap);
113 | }
114 |
115 | callback.invoke(null, fileMaps);
116 |
117 | } catch (Exception ex) {
118 | ex.printStackTrace();
119 | callback.invoke(makeErrorPayload(ex));
120 | }
121 | }
122 |
123 | @ReactMethod
124 | public void stat(String filepath, Callback callback) {
125 | try {
126 | File file = new File(filepath);
127 |
128 | if (!file.exists()) throw new Exception("File does not exist");
129 |
130 | WritableMap statMap = Arguments.createMap();
131 |
132 | statMap.putInt("ctime", (int)(file.lastModified() / 1000));
133 | statMap.putInt("mtime", (int)(file.lastModified() / 1000));
134 | statMap.putInt("size", (int)file.length());
135 | statMap.putInt("type", file.isDirectory() ? 1 : 0);
136 |
137 | callback.invoke(null, statMap);
138 | } catch (Exception ex) {
139 | ex.printStackTrace();
140 | callback.invoke(makeErrorPayload(ex));
141 | }
142 | }
143 |
144 | @ReactMethod
145 | public void unlink(String filepath, Callback callback) {
146 | try {
147 | File file = new File(filepath);
148 |
149 | if (!file.exists()) throw new Exception("File does not exist");
150 |
151 | boolean success = DeleteRecursive(file);
152 |
153 | callback.invoke(null, success, filepath);
154 | } catch (Exception ex) {
155 | ex.printStackTrace();
156 | callback.invoke(makeErrorPayload(ex));
157 | }
158 | }
159 |
160 | private boolean DeleteRecursive(File fileOrDirectory) {
161 | if (fileOrDirectory.isDirectory()) {
162 | for (File child : fileOrDirectory.listFiles()) {
163 | DeleteRecursive(child);
164 | }
165 | }
166 |
167 | return fileOrDirectory.delete();
168 | }
169 |
170 | @ReactMethod
171 | public void mkdir(String filepath, Boolean excludeFromBackup, Callback callback) {
172 | try {
173 | File file = new File(filepath);
174 |
175 | file.mkdirs();
176 |
177 | boolean success = file.exists();
178 |
179 | callback.invoke(null, success, filepath);
180 | } catch (Exception ex) {
181 | ex.printStackTrace();
182 | callback.invoke(makeErrorPayload(ex));
183 | }
184 | }
185 |
186 | private void sendEvent(ReactContext reactContext, String eventName, @Nullable WritableMap params) {
187 | reactContext
188 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
189 | .emit(eventName, params);
190 | }
191 |
192 | @ReactMethod
193 | public void downloadFile(String urlStr, final String filepath, final int jobId, final Callback callback) {
194 | try {
195 | File file = new File(filepath);
196 | URL url = new URL(urlStr);
197 |
198 | DownloadParams params = new DownloadParams();
199 |
200 | params.src = url;
201 | params.dest = file;
202 |
203 | params.onTaskCompleted = new DownloadParams.OnTaskCompleted() {
204 | public void onTaskCompleted(DownloadResult res) {
205 | if (res.exception == null) {
206 | WritableMap infoMap = Arguments.createMap();
207 |
208 | infoMap.putInt("jobId", jobId);
209 | infoMap.putInt("statusCode", res.statusCode);
210 | infoMap.putInt("bytesWritten", res.bytesWritten);
211 |
212 | callback.invoke(null, infoMap);
213 | } else {
214 | callback.invoke(makeErrorPayload(res.exception));
215 | }
216 | }
217 | };
218 |
219 | params.onDownloadBegin = new DownloadParams.OnDownloadBegin() {
220 | public void onDownloadBegin(int statusCode, int contentLength, Map headers) {
221 | WritableMap headersMap = Arguments.createMap();
222 |
223 | for (Map.Entry entry : headers.entrySet()) {
224 | headersMap.putString(entry.getKey(), entry.getValue());
225 | }
226 |
227 | WritableMap data = Arguments.createMap();
228 |
229 | data.putInt("jobId", jobId);
230 | data.putInt("statusCode", statusCode);
231 | data.putInt("contentLength", contentLength);
232 | data.putMap("headers", headersMap);
233 |
234 | sendEvent(getReactApplicationContext(), "DownloadBegin-" + jobId, data);
235 | }
236 | };
237 |
238 | params.onDownloadProgress = new DownloadParams.OnDownloadProgress() {
239 | public void onDownloadProgress(int contentLength, int bytesWritten) {
240 | WritableMap data = Arguments.createMap();
241 | data.putInt("contentLength", contentLength);
242 | data.putInt("bytesWritten", bytesWritten);
243 |
244 | sendEvent(getReactApplicationContext(), "DownloadProgress-" + jobId, data);
245 | }
246 | };
247 |
248 | Downloader downloader = new Downloader();
249 |
250 | downloader.execute(params);
251 |
252 | this.downloaders.put(jobId, downloader);
253 | } catch (Exception ex) {
254 | ex.printStackTrace();
255 | callback.invoke(makeErrorPayload(ex));
256 | }
257 | }
258 |
259 | @ReactMethod
260 | public void stopDownload(int jobId) {
261 | Downloader downloader = this.downloaders.get(jobId);
262 |
263 | if (downloader != null) {
264 | downloader.stop();
265 | }
266 | }
267 |
268 | @ReactMethod
269 | public void pathForBundle(String bundleNamed, Callback callback) {
270 | // TODO: Not sure what equilivent would be?
271 | }
272 |
273 | private WritableMap makeErrorPayload(Exception ex) {
274 | WritableMap error = Arguments.createMap();
275 | error.putString("message", ex.getMessage());
276 | return error;
277 | }
278 |
279 | @Override
280 | public Map getConstants() {
281 | final Map constants = new HashMap<>();
282 | constants.put(NSDocumentDirectory, 0);
283 | constants.put(NSDocumentDirectoryPath, this.getReactApplicationContext().getFilesDir().getAbsolutePath());
284 | constants.put(NSFileTypeRegular, 0);
285 | constants.put(NSFileTypeDirectory, 1);
286 | return constants;
287 | }
288 | }
289 |
--------------------------------------------------------------------------------
/RNFileManger.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 8BF740771C033A2E0057A1E7 /* Downloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BF740761C033A2E0057A1E7 /* Downloader.m */; };
11 | F1E59BDF1ADD662800ACA28A /* RNFileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F1E59BDE1ADD662800ACA28A /* RNFileManager.m */; };
12 | F1EB08BB1AFD0E6A008F8F2B /* NSArray+Map.m in Sources */ = {isa = PBXBuildFile; fileRef = F1EB08BA1AFD0E6A008F8F2B /* NSArray+Map.m */; };
13 | /* End PBXBuildFile section */
14 |
15 | /* Begin PBXCopyFilesBuildPhase section */
16 | F12AFB991ADAF8F800E0535D /* CopyFiles */ = {
17 | isa = PBXCopyFilesBuildPhase;
18 | buildActionMask = 2147483647;
19 | dstPath = "include/$(PRODUCT_NAME)";
20 | dstSubfolderSpec = 16;
21 | files = (
22 | );
23 | runOnlyForDeploymentPostprocessing = 0;
24 | };
25 | /* End PBXCopyFilesBuildPhase section */
26 |
27 | /* Begin PBXFileReference section */
28 | 8BF740751C033A2E0057A1E7 /* Downloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Downloader.h; sourceTree = ""; };
29 | 8BF740761C033A2E0057A1E7 /* Downloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Downloader.m; sourceTree = ""; };
30 | F12AFB9B1ADAF8F800E0535D /* libRNFileManger.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFileManger.a; sourceTree = BUILT_PRODUCTS_DIR; };
31 | F1E59BDD1ADD662800ACA28A /* RNFileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFileManager.h; sourceTree = ""; };
32 | F1E59BDE1ADD662800ACA28A /* RNFileManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFileManager.m; sourceTree = ""; };
33 | F1EB08B91AFD0E6A008F8F2B /* NSArray+Map.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Map.h"; sourceTree = ""; };
34 | F1EB08BA1AFD0E6A008F8F2B /* NSArray+Map.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Map.m"; sourceTree = ""; };
35 | /* End PBXFileReference section */
36 |
37 | /* Begin PBXFrameworksBuildPhase section */
38 | F12AFB981ADAF8F800E0535D /* Frameworks */ = {
39 | isa = PBXFrameworksBuildPhase;
40 | buildActionMask = 2147483647;
41 | files = (
42 | );
43 | runOnlyForDeploymentPostprocessing = 0;
44 | };
45 | /* End PBXFrameworksBuildPhase section */
46 |
47 | /* Begin PBXGroup section */
48 | F12AFB921ADAF8F800E0535D = {
49 | isa = PBXGroup;
50 | children = (
51 | F1EB08B91AFD0E6A008F8F2B /* NSArray+Map.h */,
52 | F1EB08BA1AFD0E6A008F8F2B /* NSArray+Map.m */,
53 | F1E59BDD1ADD662800ACA28A /* RNFileManager.h */,
54 | F1E59BDE1ADD662800ACA28A /* RNFileManager.m */,
55 | 8BF740751C033A2E0057A1E7 /* Downloader.h */,
56 | 8BF740761C033A2E0057A1E7 /* Downloader.m */,
57 | F12AFB9C1ADAF8F800E0535D /* Products */,
58 | );
59 | sourceTree = "";
60 | };
61 | F12AFB9C1ADAF8F800E0535D /* Products */ = {
62 | isa = PBXGroup;
63 | children = (
64 | F12AFB9B1ADAF8F800E0535D /* libRNFileManger.a */,
65 | );
66 | name = Products;
67 | sourceTree = "";
68 | };
69 | /* End PBXGroup section */
70 |
71 | /* Begin PBXNativeTarget section */
72 | F12AFB9A1ADAF8F800E0535D /* RNFileManger */ = {
73 | isa = PBXNativeTarget;
74 | buildConfigurationList = F12AFBAF1ADAF8F800E0535D /* Build configuration list for PBXNativeTarget "RNFileManger" */;
75 | buildPhases = (
76 | F12AFB971ADAF8F800E0535D /* Sources */,
77 | F12AFB981ADAF8F800E0535D /* Frameworks */,
78 | F12AFB991ADAF8F800E0535D /* CopyFiles */,
79 | );
80 | buildRules = (
81 | );
82 | dependencies = (
83 | );
84 | name = RNFileManger;
85 | productName = RNLocalNotification;
86 | productReference = F12AFB9B1ADAF8F800E0535D /* libRNFileManger.a */;
87 | productType = "com.apple.product-type.library.static";
88 | };
89 | /* End PBXNativeTarget section */
90 |
91 | /* Begin PBXProject section */
92 | F12AFB931ADAF8F800E0535D /* Project object */ = {
93 | isa = PBXProject;
94 | attributes = {
95 | LastUpgradeCheck = 0630;
96 | ORGANIZATIONNAME = "Johannes Lumpe";
97 | TargetAttributes = {
98 | F12AFB9A1ADAF8F800E0535D = {
99 | CreatedOnToolsVersion = 6.3;
100 | };
101 | };
102 | };
103 | buildConfigurationList = F12AFB961ADAF8F800E0535D /* Build configuration list for PBXProject "RNFileManger" */;
104 | compatibilityVersion = "Xcode 3.2";
105 | developmentRegion = English;
106 | hasScannedForEncodings = 0;
107 | knownRegions = (
108 | en,
109 | );
110 | mainGroup = F12AFB921ADAF8F800E0535D;
111 | productRefGroup = F12AFB9C1ADAF8F800E0535D /* Products */;
112 | projectDirPath = "";
113 | projectRoot = "";
114 | targets = (
115 | F12AFB9A1ADAF8F800E0535D /* RNFileManger */,
116 | );
117 | };
118 | /* End PBXProject section */
119 |
120 | /* Begin PBXSourcesBuildPhase section */
121 | F12AFB971ADAF8F800E0535D /* Sources */ = {
122 | isa = PBXSourcesBuildPhase;
123 | buildActionMask = 2147483647;
124 | files = (
125 | F1E59BDF1ADD662800ACA28A /* RNFileManager.m in Sources */,
126 | F1EB08BB1AFD0E6A008F8F2B /* NSArray+Map.m in Sources */,
127 | 8BF740771C033A2E0057A1E7 /* Downloader.m in Sources */,
128 | );
129 | runOnlyForDeploymentPostprocessing = 0;
130 | };
131 | /* End PBXSourcesBuildPhase section */
132 |
133 | /* Begin XCBuildConfiguration section */
134 | F12AFBAD1ADAF8F800E0535D /* Debug */ = {
135 | isa = XCBuildConfiguration;
136 | buildSettings = {
137 | ALWAYS_SEARCH_USER_PATHS = NO;
138 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
139 | CLANG_CXX_LIBRARY = "libc++";
140 | CLANG_ENABLE_MODULES = YES;
141 | CLANG_ENABLE_OBJC_ARC = YES;
142 | CLANG_WARN_BOOL_CONVERSION = YES;
143 | CLANG_WARN_CONSTANT_CONVERSION = YES;
144 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
145 | CLANG_WARN_EMPTY_BODY = YES;
146 | CLANG_WARN_ENUM_CONVERSION = YES;
147 | CLANG_WARN_INT_CONVERSION = YES;
148 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
149 | CLANG_WARN_UNREACHABLE_CODE = YES;
150 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
151 | COPY_PHASE_STRIP = NO;
152 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
153 | ENABLE_STRICT_OBJC_MSGSEND = YES;
154 | GCC_C_LANGUAGE_STANDARD = gnu99;
155 | GCC_DYNAMIC_NO_PIC = NO;
156 | GCC_NO_COMMON_BLOCKS = YES;
157 | GCC_OPTIMIZATION_LEVEL = 0;
158 | GCC_PREPROCESSOR_DEFINITIONS = (
159 | "DEBUG=1",
160 | "$(inherited)",
161 | );
162 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
163 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
164 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
165 | GCC_WARN_UNDECLARED_SELECTOR = YES;
166 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
167 | GCC_WARN_UNUSED_FUNCTION = YES;
168 | GCC_WARN_UNUSED_VARIABLE = YES;
169 | IPHONEOS_DEPLOYMENT_TARGET = 7.0;
170 | MTL_ENABLE_DEBUG_INFO = YES;
171 | ONLY_ACTIVE_ARCH = YES;
172 | SDKROOT = iphoneos;
173 | };
174 | name = Debug;
175 | };
176 | F12AFBAE1ADAF8F800E0535D /* Release */ = {
177 | isa = XCBuildConfiguration;
178 | buildSettings = {
179 | ALWAYS_SEARCH_USER_PATHS = NO;
180 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
181 | CLANG_CXX_LIBRARY = "libc++";
182 | CLANG_ENABLE_MODULES = YES;
183 | CLANG_ENABLE_OBJC_ARC = YES;
184 | CLANG_WARN_BOOL_CONVERSION = YES;
185 | CLANG_WARN_CONSTANT_CONVERSION = YES;
186 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
187 | CLANG_WARN_EMPTY_BODY = YES;
188 | CLANG_WARN_ENUM_CONVERSION = YES;
189 | CLANG_WARN_INT_CONVERSION = YES;
190 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
191 | CLANG_WARN_UNREACHABLE_CODE = YES;
192 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
193 | COPY_PHASE_STRIP = NO;
194 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
195 | ENABLE_NS_ASSERTIONS = NO;
196 | ENABLE_STRICT_OBJC_MSGSEND = YES;
197 | GCC_C_LANGUAGE_STANDARD = gnu99;
198 | GCC_NO_COMMON_BLOCKS = YES;
199 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
200 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
201 | GCC_WARN_UNDECLARED_SELECTOR = YES;
202 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
203 | GCC_WARN_UNUSED_FUNCTION = YES;
204 | GCC_WARN_UNUSED_VARIABLE = YES;
205 | IPHONEOS_DEPLOYMENT_TARGET = 7.0;
206 | MTL_ENABLE_DEBUG_INFO = NO;
207 | SDKROOT = iphoneos;
208 | VALIDATE_PRODUCT = YES;
209 | };
210 | name = Release;
211 | };
212 | F12AFBB01ADAF8F800E0535D /* Debug */ = {
213 | isa = XCBuildConfiguration;
214 | buildSettings = {
215 | HEADER_SEARCH_PATHS = (
216 | "$(inherited)",
217 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
218 | "$(SRCROOT)/../../React/**",
219 | "$(SRCROOT)/../react-native/React/**",
220 | "$(SRCROOT)/node_modules/react-native/React/**",
221 | );
222 | OTHER_LDFLAGS = "-ObjC";
223 | PRODUCT_NAME = RNFileManger;
224 | SKIP_INSTALL = YES;
225 | };
226 | name = Debug;
227 | };
228 | F12AFBB11ADAF8F800E0535D /* Release */ = {
229 | isa = XCBuildConfiguration;
230 | buildSettings = {
231 | HEADER_SEARCH_PATHS = (
232 | "$(inherited)",
233 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
234 | "$(SRCROOT)/../../React/**",
235 | "$(SRCROOT)/../react-native/React/**",
236 | "$(SRCROOT)/node_modules/react-native/React/**",
237 | );
238 | OTHER_LDFLAGS = "-ObjC";
239 | PRODUCT_NAME = RNFileManger;
240 | SKIP_INSTALL = YES;
241 | };
242 | name = Release;
243 | };
244 | /* End XCBuildConfiguration section */
245 |
246 | /* Begin XCConfigurationList section */
247 | F12AFB961ADAF8F800E0535D /* Build configuration list for PBXProject "RNFileManger" */ = {
248 | isa = XCConfigurationList;
249 | buildConfigurations = (
250 | F12AFBAD1ADAF8F800E0535D /* Debug */,
251 | F12AFBAE1ADAF8F800E0535D /* Release */,
252 | );
253 | defaultConfigurationIsVisible = 0;
254 | defaultConfigurationName = Release;
255 | };
256 | F12AFBAF1ADAF8F800E0535D /* Build configuration list for PBXNativeTarget "RNFileManger" */ = {
257 | isa = XCConfigurationList;
258 | buildConfigurations = (
259 | F12AFBB01ADAF8F800E0535D /* Debug */,
260 | F12AFBB11ADAF8F800E0535D /* Release */,
261 | );
262 | defaultConfigurationIsVisible = 0;
263 | defaultConfigurationName = Release;
264 | };
265 | /* End XCConfigurationList section */
266 | };
267 | rootObject = F12AFB931ADAF8F800E0535D /* Project object */;
268 | }
269 |
--------------------------------------------------------------------------------
/RNFileManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // RNFSManager.m
3 | // RNFSManager
4 | //
5 | // Created by Johannes Lumpe on 08/05/15.
6 | // Copyright (c) 2015 Johannes Lumpe. All rights reserved.
7 | //
8 |
9 | #import "RNFileManager.h"
10 | #import "RCTBridge.h"
11 | #import "NSArray+Map.h"
12 | #import "Downloader.h"
13 | #import "RCTEventDispatcher.h"
14 |
15 | @interface RNFileManager()
16 |
17 | @property (retain) NSMutableDictionary* downloaders;
18 |
19 | @end
20 |
21 | @implementation RNFileManager
22 |
23 | @synthesize bridge = _bridge;
24 |
25 | RCT_EXPORT_MODULE();
26 |
27 | - (dispatch_queue_t)methodQueue
28 | {
29 | return dispatch_queue_create("pe.lum.rnfs", DISPATCH_QUEUE_SERIAL);
30 | }
31 |
32 | RCT_EXPORT_METHOD(readDir:(NSString *)dirPath
33 | callback:(RCTResponseSenderBlock)callback)
34 | {
35 | NSFileManager *fileManager = [NSFileManager defaultManager];
36 | NSError *error = nil;
37 |
38 | NSArray *contents = [fileManager contentsOfDirectoryAtPath:dirPath error:&error];
39 |
40 | contents = [contents rnfs_mapObjectsUsingBlock:^id(NSString *obj, NSUInteger idx) {
41 | NSString *path = [dirPath stringByAppendingPathComponent:obj];
42 | NSDictionary *attributes = [fileManager attributesOfItemAtPath:path error:nil];
43 |
44 | return @{
45 | @"name": obj,
46 | @"path": path,
47 | @"size": [attributes objectForKey:NSFileSize],
48 | @"type": [attributes objectForKey:NSFileType]
49 | };
50 | }];
51 |
52 | if (error) {
53 | return callback([self makeErrorPayload:error]);
54 | }
55 |
56 | callback(@[[NSNull null], contents]);
57 | }
58 |
59 | RCT_EXPORT_METHOD(stat:(NSString *)filepath
60 | callback:(RCTResponseSenderBlock)callback)
61 | {
62 | NSError *error = nil;
63 | NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filepath error:&error];
64 |
65 | if (error) {
66 | return callback([self makeErrorPayload:error]);
67 | }
68 |
69 | attributes = @{
70 | @"ctime": [self dateToTimeIntervalNumber:(NSDate *)[attributes objectForKey:NSFileCreationDate]],
71 | @"mtime": [self dateToTimeIntervalNumber:(NSDate *)[attributes objectForKey:NSFileModificationDate]],
72 | @"size": [attributes objectForKey:NSFileSize],
73 | @"type": [attributes objectForKey:NSFileType],
74 | @"mode": @([[NSString stringWithFormat:@"%ld", (long)[(NSNumber *)[attributes objectForKey:NSFilePosixPermissions] integerValue]] integerValue])
75 | };
76 |
77 | callback(@[[NSNull null], attributes]);
78 | }
79 |
80 | RCT_EXPORT_METHOD(writeFile:(NSString *)filepath
81 | contents:(NSString *)base64Content
82 | attributes:(NSDictionary *)attributes
83 | callback:(RCTResponseSenderBlock)callback)
84 | {
85 | NSData *data = [[NSData alloc] initWithBase64EncodedString:base64Content options:NSDataBase64DecodingIgnoreUnknownCharacters];
86 | BOOL success = [[NSFileManager defaultManager] createFileAtPath:filepath contents:data attributes:attributes];
87 |
88 | if (!success) {
89 | return callback(@[[NSString stringWithFormat:@"Could not write file at path %@", filepath]]);
90 | }
91 |
92 | callback(@[[NSNull null], [NSNumber numberWithBool:success], filepath]);
93 | }
94 |
95 | RCT_EXPORT_METHOD(unlink:(NSString*)filepath
96 | callback:(RCTResponseSenderBlock)callback)
97 | {
98 | NSFileManager *manager = [NSFileManager defaultManager];
99 | BOOL exists = [manager fileExistsAtPath:filepath isDirectory:false];
100 |
101 | if (!exists) {
102 | return callback(@[[NSString stringWithFormat:@"File at path %@ does not exist", filepath]]);
103 | }
104 | NSError *error = nil;
105 | BOOL success = [manager removeItemAtPath:filepath error:&error];
106 |
107 | if (!success) {
108 | return callback([self makeErrorPayload:error]);
109 | }
110 |
111 | callback(@[[NSNull null], [NSNumber numberWithBool:success], filepath]);
112 | }
113 |
114 | RCT_EXPORT_METHOD(mkdir:(NSString*)filepath
115 | excludeFromBackup:(BOOL)excludeFromBackup
116 | callback:(RCTResponseSenderBlock)callback)
117 | {
118 | NSFileManager *manager = [NSFileManager defaultManager];
119 |
120 | NSError *error = nil;
121 | BOOL success = [manager createDirectoryAtPath:filepath withIntermediateDirectories:YES attributes:nil error:&error];
122 |
123 | if (!success) {
124 | return callback([self makeErrorPayload:error]);
125 | }
126 |
127 | NSURL *url = [NSURL fileURLWithPath:filepath];
128 |
129 | success = [url setResourceValue: [NSNumber numberWithBool: excludeFromBackup] forKey: NSURLIsExcludedFromBackupKey error: &error];
130 |
131 | if (!success) {
132 | return callback([self makeErrorPayload:error]);
133 | }
134 |
135 | callback(@[[NSNull null], [NSNumber numberWithBool:success], filepath]);
136 | }
137 |
138 | RCT_EXPORT_METHOD(readFile:(NSString *)filepath
139 | callback:(RCTResponseSenderBlock)callback)
140 | {
141 | NSData *content = [[NSFileManager defaultManager] contentsAtPath:filepath];
142 | NSString *base64Content = [content base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
143 |
144 | if (!base64Content) {
145 | return callback(@[[NSString stringWithFormat:@"Could not read file at path %@", filepath]]);
146 | }
147 |
148 | callback(@[[NSNull null], base64Content]);
149 | }
150 |
151 | RCT_EXPORT_METHOD(downloadFile:(NSString *)urlStr
152 | filepath:(NSString *)filepath
153 | jobId:(nonnull NSNumber *)jobId
154 | callback:(RCTResponseSenderBlock)callback)
155 | {
156 |
157 | DownloadParams* params = [DownloadParams alloc];
158 |
159 | params.fromUrl = urlStr;
160 | params.toFile = filepath;
161 |
162 | params.callback = ^(NSNumber* statusCode, NSNumber* bytesWritten) {
163 | if (bytesWritten == nil) {
164 | NSError *error = [NSError errorWithDomain:@"vrrv" code:0 userInfo:nil];
165 | return callback([self makeErrorPayload:error]);
166 | }
167 | return callback(@[[NSNull null], @{@"jobId": jobId,
168 | @"statusCode": (statusCode ? statusCode : [NSNumber numberWithInt:0]),
169 | @"bytesWritten": (bytesWritten ? bytesWritten : [NSNumber numberWithInt:0])}]);
170 | };
171 |
172 | params.errorCallback = ^(NSError* error) {
173 | return callback([self makeErrorPayload:error]);
174 | };
175 |
176 | params.beginCallback = ^(NSNumber* statusCode, NSNumber* contentLength, NSDictionary* headers) {
177 | [self.bridge.eventDispatcher sendAppEventWithName:[NSString stringWithFormat:@"DownloadBegin-%@", jobId]
178 | body:@{@"jobId": jobId,
179 | @"statusCode": statusCode,
180 | @"contentLength": contentLength,
181 | @"headers": headers}];
182 | };
183 |
184 | params.progressCallback = ^(NSNumber* contentLength, NSNumber* bytesWritten) {
185 | [self.bridge.eventDispatcher sendAppEventWithName:[NSString stringWithFormat:@"DownloadProgress-%@", jobId]
186 | body:@{@"contentLength": contentLength,
187 | @"bytesWritten": bytesWritten}];
188 | };
189 |
190 | if (!self.downloaders) self.downloaders = [[NSMutableDictionary alloc] init];
191 |
192 | Downloader* downloader = [Downloader alloc];
193 |
194 | [downloader downloadFile:params];
195 |
196 | [self.downloaders setValue:downloader forKey:[jobId stringValue]];
197 | }
198 |
199 | RCT_EXPORT_METHOD(stopDownload:(nonnull NSNumber *)jobId)
200 | {
201 | Downloader* downloader = [self.downloaders objectForKey:[jobId stringValue]];
202 |
203 | if (downloader != nil) {
204 | [downloader stopDownload];
205 | }
206 | }
207 |
208 | RCT_EXPORT_METHOD(exists:(NSString *)filepath
209 | callback:(RCTResponseSenderBlock)callback)
210 | {
211 | BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:filepath];
212 |
213 | callback(@[[NSNull null], [NSNumber numberWithBool:exists]]);
214 | }
215 |
216 | RCT_EXPORT_METHOD(folderExists:(NSString *)filepath
217 | callback:(RCTResponseSenderBlock)callback)
218 | {
219 | BOOL isDirectory;
220 | BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:filepath isDirectory:&isDirectory];
221 |
222 | if (exists && isDirectory) {
223 | callback(@[[NSNull null], [NSNumber numberWithBool:YES]]);
224 | } else {
225 | callback(@[[NSNull null], [NSNumber numberWithBool:NO]]);
226 | }
227 | }
228 |
229 | RCT_EXPORT_METHOD(rename:(NSString *)filepath
230 | to:(NSString *)newname
231 | callback:(RCTResponseSenderBlock)callback)
232 | {
233 | NSString *newPath = [filepath.stringByDeletingLastPathComponent stringByAppendingPathComponent:newname];
234 | NSError *error = nil;
235 | BOOL success = [[NSFileManager defaultManager] moveItemAtPath:filepath toPath:newPath error:&error];
236 |
237 | callback(@[[NSNull null], [NSNumber numberWithBool:success]]);
238 | }
239 |
240 | RCT_EXPORT_METHOD(moveFile:(NSString *)filepath
241 | to:(NSString *)newPath
242 | callback:(RCTResponseSenderBlock)callback)
243 | {
244 | NSError *error = nil;
245 | BOOL success = [[NSFileManager defaultManager] moveItemAtPath:filepath toPath:newPath error:&error];
246 |
247 | callback(@[[NSNull null], [NSNumber numberWithBool:success]]);
248 | }
249 |
250 | RCT_EXPORT_METHOD(pathForBundle:(NSString *)bundleNamed
251 | callback:(RCTResponseSenderBlock)callback)
252 | {
253 | NSString *path = [[NSBundle mainBundle].bundlePath stringByAppendingFormat:@"/%@.bundle", bundleNamed];
254 | NSBundle *bundle = [NSBundle bundleWithPath:path];
255 |
256 | if (!bundle) {
257 | bundle = [NSBundle bundleForClass:NSClassFromString(bundleNamed)];
258 | path = bundle.bundlePath;
259 | }
260 |
261 | if (!bundle.isLoaded) {
262 | [bundle load];
263 | }
264 |
265 | if (path) {
266 | callback(@[[NSNull null], path]);
267 | } else {
268 | callback(@[[NSError errorWithDomain:NSPOSIXErrorDomain
269 | code:NSFileNoSuchFileError
270 | userInfo:nil].localizedDescription,
271 | [NSNull null]]);
272 | }
273 | }
274 |
275 | - (NSNumber *)dateToTimeIntervalNumber:(NSDate *)date
276 | {
277 | return @([date timeIntervalSince1970]);
278 | }
279 |
280 | - (NSArray *)makeErrorPayload:(NSError *)error
281 | {
282 | return @[@{
283 | @"description": error.localizedDescription,
284 | @"code": @(error.code)
285 | }];
286 | }
287 |
288 | - (NSString *)getPathForDirectory:(int)directory
289 | {
290 | NSArray *paths = NSSearchPathForDirectoriesInDomains(directory, NSUserDomainMask, YES);
291 | return [paths firstObject];
292 | }
293 |
294 | - (NSDictionary *)constantsToExport
295 | {
296 | return @{
297 | @"MainBundlePath": [[NSBundle mainBundle] bundlePath],
298 | @"NSCachesDirectoryPath": [self getPathForDirectory:NSCachesDirectory],
299 | @"NSDocumentDirectoryPath": [self getPathForDirectory:NSDocumentDirectory],
300 | @"NSApplicationSupportDirectoryPath": [self getPathForDirectory:NSApplicationSupportDirectory],
301 | @"NSFileTypeRegular": NSFileTypeRegular,
302 | @"NSFileTypeDirectory": NSFileTypeDirectory
303 | };
304 | }
305 |
306 | @end
307 |
--------------------------------------------------------------------------------