UNPKG

11.1 kBMarkdownView Raw
1# react-native-document-picker
2
3⚠️ Breaking: New Interface: use v2 branch if you need to go back.
4PR are welcome to add what's missing.
5
6A React Native wrapper for:
7
8- Apple's `UIDocumentMenuViewController`
9- Android's `Intent.ACTION_OPEN_DOCUMENT` / `Intent.ACTION_PICK`
10- Windows `Windows.Storage.Pickers`
11
12### Installation
13
14```bash
15npm i --save react-native-document-picker
16```
17
18**Automatically Link Native Modules**
19
20Link native packages via the following command:
21
22```
23react-native link
24```
25
26**Manually Link Native Modules**
27
281. Run `npm install react-native-document-picker --save`
292. Open your project in XCode, right click on `Libraries` and click `Add Files to "Your Project Name"` [(Screenshot)](http://url.brentvatne.ca/jQp8) then [(Screenshot)](http://url.brentvatne.ca/1gqUD).
303. Add `libRNDocumentPicker.a` to `Build Phases -> Link Binary With Libraries`
31 [(Screenshot)](http://url.brentvatne.ca/17Xfe).
32
33**CocoaPods**
34
35Add the following to your podfile:
36
37```
38pod 'react-native-document-picker', :path => '../node_modules/react-native-document-picker`
39```
40
41### Android
42
43```gradle
44// file: android/settings.gradle
45...
46
47include ':react-native-document-picker'
48project(':react-native-document-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-document-picker/android')
49```
50
51```gradle
52// file: android/app/build.gradle
53...
54
55dependencies {
56 ...
57 compile project(':react-native-document-picker')
58}
59```
60
61```java
62// file: MainApplication.java
63...
64
65import io.github.elyx0.reactnativedocumentpicker.DocumentPickerPackage; // Import package
66
67public class MainApplication extends Application implements ReactApplication {
68
69 /**
70 * A list of packages used by the app. If the app uses additional views
71 * or modules besides the default ones, add more packages here.
72 */
73 @Override
74 protected List<ReactPackage> getPackages() {
75 return Arrays.<ReactPackage>asList(
76 new MainReactPackage(),
77 new DocumentPickerPackage() // Add package
78 );
79 }
80...
81}
82```
83
84### Windows
85
86Follow the instructions in the ['Linking Libraries'](https://github.com/Microsoft/react-native-windows/blob/master/docs/LinkingLibrariesWindows.md) documentation on the react-native-windows GitHub repo. For the first step of adding the project to the Visual Studio solution file, the path to the project should be `../node_modules/react-native-document-picker/windows/RNDocumentPicker/RNDocumentPicker.csproj`.
87
88## API
89
90### `DocumentPicker.pick(opts)` and `DocumentPicker.pickMultiple(opts)`
91
92Use `pick` or `pickMultiple` to open a document picker for the user to select file(s). Both methods return a Promise. `pick` will only allow a single selection and the Promise will resolve to that single result. `pickMultiple` will allow multiple selection and the Promise returned will always resolve to an array of results.
93
94**Options:**
95
96- **`type`**:`string|Array<string>`: The type or types of documents to allow selection of. May be an array of types as single type string.
97 - On Android these are MIME types such as `text/plain` or partial MIME types such as `image/*`.
98 - On iOS these must be Apple "[Uniform Type Identifiers](https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html)"
99 - If `type` is omitted it will be treated as `*/*` or `public.content`.
100 - Multiple type strings are not supported on Android before KitKat (API level 19), Jellybean will fall back to `*/*` if you provide an array with more than one value.
101- **[UWP only] `readContent`**: Boolean which defaults to `false`. If `readContent` is set to true the content of the picked file/files will be read and supplied in the result object.
102
103 - Be aware that this can introduce a huge performance hit in case of big files. (The files are read completely and into the memory and encoded to base64 afterwards to add them to the result object)
104 - However reading the file directly from within the Thread which managed the picker can be necessary on Windows: Windows Apps can only read the Downloads folder and their own app folder by default and If a file is outside of these locations it cannot be acessed directly. However if the user picks the file through a file picker permissions to that file are granted implicitly.
105
106 ```
107 In addition to the default locations, an app can access additional files and folders by declaring capabilities in the app manifest (see App capability declarations), or by calling a file picker to let the user pick files and folders for the app to access (see Open files and folders with a picker).
108 ```
109
110 https://docs.microsoft.com/en-us/windows/uwp/files/file-access-permissions
111
112 Unfortunately that permission is not granted to the whole app, but only the Thread which handled the filepicker. Therefore it can be useful to read the file directly.
113
114 - You can use `react-native-fs` on Android and IOS to read the picked file.
115
116**Result:**
117
118The object a `pick` Promise resolves to or the objects in the array a `pickMultiple` Promise resolves to will contain the following keys.
119
120- **`uri`**: The URI representing the document picked by the user. _On iOS this will be a `file://` URI for a temporary file in your app's container. On Android this will be a `content://` URI for a document provided by a DocumentProvider that must be accessed with a ContentResolver._
121- **`type`**: The MIME type of the file. _On Android some DocumentProviders may not provide MIME types for their documents. On iOS this MIME type is based on the best MIME type for the file extension according to Apple's internal "Uniform Type Identifiers" database._
122- **`name`**: The display name of the file. _This is normally the filename of the file, but Android does not guarantee that this will be a filename from all DocumentProviders._
123- **`size`**: The file size of the document. _On Android some DocumentProviders may not provide this information for a document._
124- **[UWP only] `content`**: The base64 encoded content of the picked file if the option `readContent` was set to `true`.
125
126### `DocumentPicker.types.*`
127
128`DocumentPicker.types.*` provides a few common types for use as `type` values, these types will use the correct format for each platform (MIME types on Android, UTIs on iOS).
129
130- `DocumentPicker.types.allFiles`: All document types, on Android this is `*/*`, on iOS is is `public.content` (note that some binary and archive types do not inherit from `public.content`)
131- `DocumentPicker.types.images`: All image types (`image/*` or `public.image`)
132- `DocumentPicker.types.plainText`: Plain text files ie: `.txt` (`text/plain` or `public.plain-text`)
133- `DocumentPicker.types.audio`: All audio types (`audio/*` or `public.audio`)
134- `DocumentPicker.types.pdf`: PDF documents (`application/pdf` or `com.adobe.pdf`)
135
136### `DocumentPicker.isCancel(err)`
137
138If the user cancels the document picker without choosing a file (by pressing the system back button on Android or the Cancel button on iOS) the Promise will be rejected with a cancellation error. You can check for this error using `DocumentPicker.isCancel(err)` allowing you to ignore it and cleanup any parts of your interface that may not be needed anymore.
139
140## Example
141
142```javascript
143import DocumentPicker from 'react-native-document-picker';
144
145// Pick a single file
146try {
147 const res = await DocumentPicker.pick({
148 type: [DocumentPicker.types.images],
149 });
150 console.log(
151 res.uri,
152 res.type, // mime type
153 res.name,
154 res.size
155 );
156} catch (err) {
157 if (DocumentPicker.isCancel(err)) {
158 // User cancelled the picker, exit any dialogs or menus and move on
159 } else {
160 throw err;
161 }
162}
163
164// Pick multiple files
165try {
166 const results = await DocumentPicker.pickMultiple({
167 type: [DocumentPicker.types.images],
168 });
169 for (const res of results) {
170 console.log(
171 res.uri,
172 res.type, // mime type
173 res.name,
174 res.size
175 );
176 }
177} catch (err) {
178 if (DocumentPicker.isCancel(err)) {
179 // User cancelled the picker, exit any dialogs or menus and move on
180 } else {
181 throw err;
182 }
183}
184```
185
186## Here is how it looks:
187
188![screenshot](http://i.stack.imgur.com/dv0iQ.png)
189
190## How to send it back ?
191
192I recommend using [https://github.com/johanneslumpe/react-native-fs](https://github.com/johanneslumpe/react-native-fs)
193I had to modify [Uploader.m](https://gist.github.com/Elyx0/5dc53bef294b42c847f1baea7cc5e911) so it would use `NSFileCoordinator` with `NSFileCoordinatorReadingForUploading` option.
194
195I added a check for file length that would be thrown into RNFS catch block.
196
197```obj-c
198if ([fileData length] == 0) {
199 NSError *errorUp = [NSError errorWithDomain:@"com.whatever.yourapp" code:77 userInfo:[NSDictionary dictionaryWithObject:@"empty" forKey:NSLocalizedDescriptionKey]];
200 _params.errorCallback(errorUp);
201 return;
202}
203```
204
205```javascript
206let url = "file://whatever/com.bla.bla/file.ext"; //The url you received from the DocumentPicker
207
208// I STRONGLY RECOMMEND ADDING A SMALL SETTIMEOUT before uploading the url you just got.
209const split = url.split('/');
210const name = split.pop();
211const inbox = split.pop();
212const realPath = `${RNFS.TemporaryDirectoryPath}${inbox}/${name}`;
213
214const uploadBegin = (response) => {
215 const jobId = response.jobId;
216 console.log('UPLOAD HAS BEGUN! JobId: ' + jobId);
217};
218
219const uploadProgress = (response) => {
220 const percentage = Math.floor((response.totalBytesSent/response.totalBytesExpectedToSend) * 100);
221 console.log('UPLOAD IS ' + percentage + '% DONE!');
222};
223
224RNFS.uploadFiles({
225 toUrl: uploadUrl,
226 files: [{
227 name,
228 filename:name,
229 filepath: realPath,
230 }],
231 method: 'POST',
232 headers: {
233 'Accept': 'application/json',
234 },
235 begin: uploadBegin,
236 beginCallback: uploadBegin, // Don't ask me, only way I made it work as of 1.5.1
237 progressCallback: uploadProgress,
238 progress: uploadProgress
239 })
240 .then((response) => {
241 console.log(response,"<<< Response");
242 if (response.statusCode == 200) { //You might not be getting a statusCode at all. Check
243 console.log('FILES UPLOADED!');
244 } else {
245 console.log('SERVER ERROR');
246 }
247 })
248 .catch((err) => {
249 if (err.description) {
250 switch (err.description) {
251 case "cancelled":
252 console.log("Upload cancelled");
253 break;
254 case "empty"
255 console.log("Empty file");
256 default:
257 //Unknown
258 }
259 } else {
260 //Weird
261 }
262 console.log(err);
263 });
264```
265
266## Reminder
267
268You need to enable iCloud Documents to access iCloud
269![screen](https://313e5987718b346aaf83-f5e825270f29a84f7881423410384342.ssl.cf1.rackcdn.com/1411920674-enable-icloud-drive.png)
270
271## Halp wanted: Improvements
272
273- Fix Xcode warning about constraints
274- support options for the [UIDocumentPickerViewController](https://developer.apple.com/library/ios/documentation/FileManagement/Conceptual/DocumentPickerProgrammingGuide/AccessingDocuments/AccessingDocuments.html#//apple_ref/doc/uid/TP40014451-CH2-SW5)
275- Handle Upload by itself ?