Open xpalacincreditoh opened 2 years ago
Hi,
I've exactly the same problem. From the Download directory I can read a JPG file but not a PDF file (permission denied) @xpalacincreditoh Did you find a solution ?
Thanks
I'm waiting for you to merge the PR pending fix https://github.com/NativeScript/NativeScript/pull/9661
// REVIEW: Not sure why DocumentsContact is not there yet
type ProviderWithDocumentsContact = typeof android.provider & {
DocumentsContract: any;
};
// Per https://stackoverflow.com/questions/17546101/get-real-path-for-uri-android
export function getPathFromURI(uri: android.net.Uri) {
const getMediaFilePathForN = (uri: android.net.Uri, context: any) => {
let returnUri = uri;
let returnCursor = context
.getContentResolver()
.query(returnUri, null, null, null, null);
/*
* Get the column indexes of the data in the Cursor,
* * move to the first row in the Cursor, get the data,
* * and display it.
* */
let nameIndex = returnCursor.getColumnIndex(
android.provider.OpenableColumns.DISPLAY_NAME
);
let sizeIndex = returnCursor.getColumnIndex(
android.provider.OpenableColumns.SIZE
);
returnCursor.moveToFirst();
let name = returnCursor.getString(nameIndex);
let size = java.lang.Long.toString(returnCursor.getLong(sizeIndex));
let file = new java.io.File(context.getFilesDir(), name);
try {
let inputStream = context.getContentResolver().openInputStream(uri);
let outputStream = new java.io.FileOutputStream(file);
let read = 0;
let maxBufferSize = 1 * 1024 * 1024;
let bytesAvailable = inputStream.available();
//int bufferSize = 1024;
let bufferSize = Math.min(bytesAvailable, maxBufferSize);
let buffers = java.lang.reflect.Array.newInstance(
java.lang.Byte.class.getField('TYPE').get(null),
bufferSize
);
while ((read = inputStream.read(buffers)) != -1) {
outputStream.write(buffers, 0, read);
}
inputStream.close();
outputStream.close();
console.log('File Path', 'Path ' + file.getPath());
console.log('File Size', 'Size ' + file.length());
} catch (e) {
console.log('Exception', e);
}
return file.getPath();
};
const getDriveFilePath = (uri: android.net.Uri, context: any) => {
let returnUri = uri;
let returnCursor = context
.getContentResolver()
.query(returnUri, null, null, null, null);
/*
* Get the column indexes of the data in the Cursor,
* * move to the first row in the Cursor, get the data,
* * and display it.
* */
let nameIndex = returnCursor.getColumnIndex(
android.provider.OpenableColumns.DISPLAY_NAME
);
returnCursor.moveToFirst();
let name = returnCursor.getString(nameIndex);
let file = new java.io.File(context.getCacheDir(), name);
try {
let inputStream = context.getContentResolver().openInputStream(uri);
let outputStream = new java.io.FileOutputStream(file);
let read = 0;
let maxBufferSize = 1 * 1024 * 1024;
let bytesAvailable = inputStream.available();
//int bufferSize = 1024;
let bufferSize = Math.min(bytesAvailable, maxBufferSize);
let buffers = java.lang.reflect.Array.newInstance(
java.lang.Byte.class.getField('TYPE').get(null),
bufferSize
);
while ((read = inputStream.read(buffers)) != -1) {
outputStream.write(buffers, 0, read);
}
inputStream.close();
outputStream.close();
console.log('File Path', 'Path ' + file.getPath());
console.log('File Size', 'Size ' + file.length());
} catch (e) {
console.log('Exception', e);
}
return file.getPath();
};
const getDataColumn = (
context: any,
uri: android.net.Uri,
selection: any,
selectionArgs: any
) => {
let cursor = null;
const column = '_data';
const projection = [column];
try {
cursor = context
.getContentResolver()
.query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
let column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} catch (e) {
return getMediaFilePathForN(uri, context);
} finally {
if (cursor != null) cursor.close();
}
return null;
};
const isExternalStorageDocument = (uri: android.net.Uri) => {
return 'com.android.externalstorage.documents' === uri.getAuthority();
};
const isDownloadsDocument = (uri: android.net.Uri) => {
return 'com.android.providers.downloads.documents' === uri.getAuthority();
};
const isMediaDocument = (uri: android.net.Uri) => {
return 'com.android.providers.media.documents' === uri.getAuthority();
};
const isGooglePhotosUri = (uri: android.net.Uri) => {
return 'com.google.android.apps.photos.content' === uri.getAuthority();
};
const isGoogleDriveUri = (uri: android.net.Uri) => {
return (
'com.google.android.apps.docs.storage' === uri.getAuthority() ||
'com.google.android.apps.docs.storage.legacy' === uri.getAuthority()
);
};
const activity =
Application.android.startActivity || Application.android.foregroundActivity;
const context = activity.getApplicationContext();
const isKitKat = parseInt(Device.sdkVersion, 10) >= 19;
if (typeof uri === 'string') {
uri = android.net.Uri.parse(uri);
}
// DocumentProvider
if (
isKitKat &&
(<ProviderWithDocumentsContact>(
android.provider
)).DocumentsContract.isDocumentUri(context, uri)
) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
const docId: string = (<ProviderWithDocumentsContact>(
android.provider
)).DocumentsContract.getDocumentId(uri);
const split = docId.split(':');
const type: string = split[0].toLowerCase();
if ('primary' === type) {
return (
android.os.Environment.getExternalStorageDirectory() + '/' + split[1]
);
} else {
// https://stackoverflow.com/questions/44226029/how-get-a-file-path-by-uri-which-authority-is-com-android-externalstorage-docum
let external = context.getExternalMediaDirs();
if (external.length > 0) {
let filePath = external[0].getAbsolutePath();
filePath =
filePath.substring(0, filePath.indexOf('Android')) + split[1];
return filePath;
}
return uri.getPath();
}
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
if (parseInt(Device.sdkVersion, 10) >= 23) {
let cursor = null;
try {
cursor = context
.getContentResolver()
.query(
uri,
[android.provider.MediaStore.MediaColumns.DISPLAY_NAME],
null,
null,
null
);
if (cursor != null && cursor.moveToFirst()) {
let fileName = cursor.getString(0);
let path =
android.os.Environment.getExternalStorageDirectory().toString() +
'/Download/' +
fileName;
if (!android.text.TextUtils.isEmpty(path)) {
return path;
}
}
} finally {
if (cursor != null) cursor.close();
}
const id = (<ProviderWithDocumentsContact>(
android.provider
)).DocumentsContract.getDocumentId(uri);
if (!android.text.TextUtils.isEmpty(id)) {
if (id.startsWith('raw:')) {
return id.replace(/^raw:/, '');
}
const contentUriPrefixesToTry = [
'content://downloads/public_downloads',
'content://downloads/my_downloads'
];
for (let contentUriPrefix of contentUriPrefixesToTry) {
try {
let contentUri = android.content.ContentUris.withAppendedId(
android.net.Uri.parse(contentUriPrefix),
java.lang.Long.valueOf(id) as any
);
/* final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));*/
return getDataColumn(context, contentUri, null, null);
} catch (e) {
//In Android 8 and Android P the id is not a number
return uri
.getPath()
.replace(/^\/document\/raw:/, '')
.replace(/^raw:/, '');
}
}
}
} else {
const id: string = (<ProviderWithDocumentsContact>(
android.provider
)).DocumentsContract.getDocumentId(uri);
if (id.startsWith('raw:')) {
return id.slice(4);
} else {
const contentUri = android.content.ContentUris.withAppendedId(
android.net.Uri.parse('content://downloads/public_downloads'),
java.lang.Long.valueOf(id) as any
);
return getDataColumn(context, contentUri, null, null);
}
}
}
// MediaProvider
else if (isMediaDocument(uri)) {
const docId = (<ProviderWithDocumentsContact>(
android.provider
)).DocumentsContract.getDocumentId(uri);
const split = docId.split(':');
const type = split[0];
let contentUri = null;
if ('image' === type) {
contentUri =
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ('video' === type) {
contentUri =
android.provider.MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ('audio' === type) {
contentUri =
android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
} else {
return getMediaFilePathForN(uri, context);
}
const selection = '_id=?';
const selectionArgs = [split[1]]; // js Array?
return getDataColumn(context, contentUri, selection, selectionArgs);
} else if (isGoogleDriveUri(uri)) {
return getDriveFilePath(uri, context);
}
}
// MediaStore (and general)
else if ('content' === uri.getScheme().toLowerCase()) {
if (isGooglePhotosUri(uri)) {
return uri.getLastPathSegment();
}
if (isGoogleDriveUri(uri)) {
return getDriveFilePath(uri, context);
}
if (parseInt(Device.sdkVersion) === 24) {
// return getFilePathFromURI(context,uri);
return getMediaFilePathForN(uri, context);
// return getRealPathFromURI(context,uri);
} else {
return getDataColumn(context, uri, null, null);
}
}
// File
else if ('file' === uri.getScheme().toLowerCase()) {
return uri.getPath();
}
return null;
}
@xpalacincreditoh I use this monstrosity for reading files for now. You might only need getMediaFilePathForN
from there. But it seems to handle most of things.
When I call:
openFilePicker(isIOS ? this.optionsFile.ios : this.optionsFile.android).then((res) => { ...
Where do I have to use your method getPathFromURI?
@xpalacincreditoh res.android
@xpalacincreditoh
res.android
When I use your code I get an error with word type:
Module parse failed: Unexpected token (279:5)
File was processed with these loaders:
* ./node_modules/@nativescript/webpack/dist/loaders/nativescript-worker-loader/index.js
You may need an additional loader to handle the result of these loaders.
|
| // REVIEW: Not sure why DocumentsContact is not there yet
> type ProviderWithDocumentsContact = typeof android.provider & {
| DocumentsContract: any;
| };
@xpalacincreditoh seems like you are using typescript code in a JS file?
@xpalacincreditoh seems like you are using typescript code in a JS file?
yes I use your solution of yesterday https://github.com/nativescript-community/ui-document-picker/issues/17#issuecomment-1011052259 I just realized it's TS and my code is JS.
Error:
JS: TypeError: getUtils(...).getBytes is not a function
Good morning,
My system details:
When Open filepicker and select a PDF file from downloads folder the file.readSync sayas error:
And when call file.read says error:
This just happens only when targetSdkVersion is set to 30 and not in 29, but now to publish and app in Google App Store needs targetSdkVersion 30.
Thanks