nativescript-community / ui-document-picker

A NativeScript plugin that allows you to select files from the device.
https://nativescript-community.github.io/ui-document-picker/
Apache License 2.0
10 stars 8 forks source link

ui-document-picker returns file path that can't be used to open a file #21

Open BikeBr0 opened 2 years ago

BikeBr0 commented 2 years ago

On both iOS and Android I cannot open the file using the path returned by openFilePicker. For example, using {N} 8.1.5:

ns create test-app
> Angular
> Hello World
ns plugin add @nativescript-community/ui-document-picker

Then edit src/app/item/item-detail.component.html to open a filePicker:

...
<Label class="h2" [text]="item.name" (tap)="handleTap()"></Label>
...

And add the corresponding handler to src/app/item/item-detail.component.ts:

...
  handleTap(): void {
    openFilePicker().then((val) => {
      if (!val.files || val.files.length === 0) {
        throw new Error('No file selected.');
      } else {
        const path = val.files[0];
        console.log(`Path: ${path}`);
        const file = File.fromPath(path);
        console.log(`File: ${file.name}`);
      }
    });
  }
...

Then:

ns build android
ns run android

And test on the device.

Expected result: File name is printed to the console

Actual result: ERROR Error: Uncaught (in promise): Error: java.io.IOException: No such file or directory

Here is a more detailed stack trace:

JS: ERROR Error: Uncaught (in promise): Error: java.io.IOException: No such file or directory
JS: ensureFile(file: src\webpack:\test-app\node_modules\@nativescript\core\file-system\file-system-access.android.js:388:0)
JS:     at getFile(file: src\webpack:\test-app\node_modules\@nativescript\core\file-system\file-system-access.android.js:41:0)
JS:     at fromPath(file: src\webpack:\test-app\node_modules\@nativescript\core\file-system\index.js:136:0)
JS:     at (file: src\webpack:\test-app\src\app\item\item-detail.component.ts:26:29)
JS:     at invoke(file: src\webpack:\test-app\node_modules\zone.js\fesm2015\zone.js:372:0)
JS:     at onInvoke(file: src\webpack:\test-app\node_modules\@angular\core\fesm2015\core.mjs:25457:0)
JS:     at invoke(file: src\webpack:\test-app\node_modules\zone.js\fesm2015\zone.js:371:0)
JS:     at run(file: src\webpack:\test-app\node_modules\zone.js\fesm2015\zone.js:134:0)
JS:     at (file: src\webpack:\test-app\node_modules\zone.js\fesm2015\zone.js:1276:0)
JS:     at invokeTask(file: src\webpack:\test-app\node_modules\zone.js\fesm2015\zone.js:406:0)
JS:     at onInvokeTask(file: src\webpack:\test-app\node_modules\@angular\core\fesm2015\core.mjs:25444:0)
JS:     at invokeTas...

The path returned on Android is, "content://com.android.providers.media.documents/document/image%3A122" and on iOS is "file:///private/var/mobile/Containers/Shared/AppGroup/1EA300C8-AE93-45E0-BE2F-4CF58C832464/File%20Provider%20Storage/Downloads/some-file.hex"

farfromrefug commented 2 years ago

@BikeBr0 you ll need to give more details. code, logs...

BikeBr0 commented 2 years ago

Yep, I'm working on that. Sorry for the delay.

farfromrefug commented 2 years ago

@BikeBr0 android 12?

BikeBr0 commented 2 years ago

Broken on Android 12 and iOS 15.2. Works on Android 8.1. iOS 12.5 is acting weird in general...

farfromrefug commented 2 years ago

@BikeBr0 sadly the error is on N side. N does not support android 12 scope storage (opening files outside of the scope of your app). ios dont know but again seems to be on N side. would need the ios error log but i think it might be N. or maybe a permission ?

BikeBr0 commented 2 years ago

Thanks @farfromrefug. I can live without Android support but I really need to get this feature working on iOS. I'll add more comments with my iOS debug info.

BikeBr0 commented 2 years ago

After resolving xCode issues, I was able to get my test-app to run on iOS again.

iOS 12.5

iOS 15.2

Are extensions mandatory on iOS? Is it not possible to select any file?

farfromrefug commented 2 years ago

@BikeBr0 thanks for the details! should be fixed in 1.1.12

BikeBr0 commented 2 years ago

Great! 1.1.12 fixed the undefined error when not supplying the FilePickerOptions to openFilePicker().

Even better, I am able to open files on iOS. The secret is to remove the leading 'file://' from the path:

openFilePicker().then((val) => {
    console.log(JSON.stringify(val));

    if (!val.files || val.files.length === 0) {
        throw new Error('No file selected.');
    } else {
        const suppliedPath = val.files[0];
        console.log(`suppliedPath: ${suppliedPath}`);

        const scrubbedPath = suppliedPath.replace('file://', '');
        console.log(`scrubbedPath: ${scrubbedPath}`);

        const file = File.fromPath(scrubbedPath);
        console.log(`File: ${file.name}`);
    }
});

This will allow you to browse the filesystem and select well known files (images, txt files, etc.). If you need to access uncommon/proprietary file types, you will need to define UTIs in App_Resources/iOS/Info.plist. Here's an example of a binary file type:

<key>UTExportedTypeDeclarations</key>
<array>
    <dict>
        <key>UTTypeIdentifier</key>
        <string>YOUR_IDENTIFIER_HERE</string>
        <key>UTTypeConformsTo</key>
        <array>
            <string>public.data</string>
        </array>
        <key>UTTypeDescription</key>
        <string>YOU_DESCRIPTION_HERE</string>
        <key>UTTypeTagSpecification</key>
        <dict>
            <key>public.filename-extension</key>
            <array>
                <string>EXTENSION</string>
            </array>
            <key>public.mime-type</key>
            <string>application/octet</string>
        </dict>
    </dict>
</array>

And the code that uses it:

let extensions = [];
if (isIOS) {
    extensions = ['YOUR_IDENTIFIER_HERE'];
} else {
    // Do something else in the future when {N} supports it...
}

openFilePicker({extensions: extensions})

This should let the user browse the file system but only select a file specified by extensions.

UPDATE: This doesn't work for all users. Some users (not iOS version dependent) cannot select the file specified by the UTI.

ray007 commented 2 years ago

Just tested on iOS 15.3.1 with version 1.1.12 and the file is greyed out and not selectable.

farfromrefug commented 2 years ago

@ray007 please you will need to give more details. Saying it does not work wont really allow us to help you.

ray007 commented 2 years ago

@farfromrefug what information would you like me to provide? I've tried on iOS with

let files = await openFilePicker({extensions: ['json'], multipleSelection: false});

and I can browse to the file I want to select, but it's greyed out and I cannot choose it.

farfromrefug commented 2 years ago

@ray007 that ;) On ios you can do what you are trying to do. You need to use https://developer.apple.com/documentation/uniformtypeidentifiers/system_declared_uniform_type_identifiers?language=objc

ray007 commented 2 years ago

Apple really seems to hate casual apple-developers.

rubenhr commented 1 month ago

Is this still worked on?

farfromrefug commented 1 month ago

@rubenhr yes and it is all working. On iOS the issue is on N side. N does not support path with file:// at the beginning. You can manually remove it. On Android it should work perfectly. The only issue might be that if the file picker returns content:// on old android versions then N does not support it (when it could).