Cordova: How to move file to the Download folder? - android

THE SITUATION:
In my mobile app I need to download a file and store in the Download folder.
The download part is working fine.
The file is properly downloaded from the server and stored in the following folder:
file:///storage/emulated/0/Android/data/org.cordova.MY_APP_NAME.app/my_file.pdf
But the location is not really user-friendly.
To access it I have to go to: Internal storage / Android / data / org.cordova.MY_APP_NAME.app /
So I need to move it to the main Download folder.
The file transfer is what I don't manage to do.
I know that there are already several similar questions on SO.
I have tried them all but none really worked for me, I could never see the file in the actual Download folder.
PROJECT INFO:
I am using Quasar with Vuejs and Cordova.
PLATFORM:
For the moment I am working with Android. But ideally I am looking for a solution that works for both Android and IOS.
THE CODE:
The download code:
var fileTransfer = new FileTransfer() // eslint-disable-line
var uri = encodeURI('https://MY_SERVER_PATH')
fileTransfer.download(
uri,
cordova.file.externalApplicationStorageDirectory + 'my_file.pdf',
entry => {
console.log('download complete: ' + entry.toURL())
this.moveFile(entry.toURL())
},
error => {
console.log('download error source ' + error.source)
console.log('download error target ' + error.target)
console.log('download error code' + error.code)
},
false,
{
headers: {
'Authorization': 'Basic asdasdasdasdassdasdasd'
}
}
)
The File transfer code:
moveFile(fileUri) {
window.resolveLocalFileSystemURL(
fileUri,
fileEntry => {
let newFileUri = 'file:///storage/emulated/0/Download'
window.resolveLocalFileSystemURL(
newFileUri,
dirEntry => {
fileEntry.moveTo(dirEntry, 'new_filename.pdf', this.moveFileSuccess, this.moveFileError)
},
this.moveFileError)
},
this.moveFileError)
},
moveFileSuccess(entry) {
console.log('file move success')
console.log(entry)
},
moveFileError(error) {
console.log('file move error')
console.log(error)
}
THE QUESTION:
How can I move a file to the Download folder?
Thanks
EDIT:
This is the console log of the cordova.file object:
applicationDirectory: "file:///android_asset/"
applicationStorageDirectory: "file:///data/user/0/org.cordova.MY_APP_NAME.app/"
cacheDirectory:"file:///data/user/0/org.cordova.MY_APP_NAME.app/cache/"
dataDirectory: "file:///data/user/0/org.cordova.MY_APP_NAME.app/files/"
documentsDirectory: null
externalApplicationStorageDirectory: "file:///storage/emulated/0/Android/data/org.cordova.MY_APP_NAME.app/"
externalCacheDirectory: "file:///storage/emulated/0/Android/data/org.cordova.MY_APP_NAME.app/cache/"
externalDataDirectory: "file:///storage/emulated/0/Android/data/org.cordova.MY_APP_NAME.app/files/"
externalRootDirectory: "file:///storage/emulated/0/"
sharedDirectory: null
syncedDataDirectory: null
tempDirectory: null

Okay I managed to resolve it.
First of all is totally unnecessary to download and then move the file. It can just be directly downloaded in the desired direction.
The correct path (in my case) was this:
cordova.file.externalRootDirectory + 'download/' + 'my_file.pdf
that correspond to: file:///storage/emulated/0/download/my_file.pdf
and that means that to find the file inside the device you have to go to: Internal Storage / Download / my_file.pdf
Add the following value in the config.xml:
<preference name="AndroidPersistentFileLocation" value="Compatibility" />
<preference name="AndroidExtraFilesystems" value="files,files-external,documents,sdcard,root" />
It's important to check for permission using this cordova plugin: cordova-plugin-android-permissions
You can make a quick test like this:
let permissions = cordova.plugins.permissions
permissions.checkPermission(permissions.READ_EXTERNAL_STORAGE, checkPermissionCallback, null)
function checkPermissionCallback(status) {
console.log('checking permissions')
console.log(status)
}
Most probably the result is false. And that means that we have to request permission to the user:
permissions.requestPermission(successCallback, errorCallback, permission)
In this way it will appear the alert asking for permission.
THE CODE:
To put it all together, this is the working code:
let pdfPath = 'https://MY_SERVER_PATH'
let permissions = cordova.plugins.permissions
permissions.checkPermission(permissions.READ_EXTERNAL_STORAGE, checkPermissionCallback, null)
// Checking for permissions
function checkPermissionCallback(status) {
console.log('checking permissions')
console.log(status)
if (!status.hasPermission) {
var errorCallback = function () {
console.warn('Storage permission is not turned on')
}
// Asking permission to the user
permissions.requestPermission(
permissions.READ_EXTERNAL_STORAGE,
function (status) {
if (!status.hasPermission) {
errorCallback()
} else {
// proceed with downloading
downloadFile()
}
},
errorCallback)
} else {
downloadFile()
}
}
function downloadFile() {
let filePath = cordova.file.externalRootDirectory + 'download/' + 'my_file.pdf'
let fileTransfer = new window.FileTransfer()
let uri = encodeURI(decodeURIComponent(pdfPath))
// Downloading the file
fileTransfer.download(uri, filePath,
function (entry) {
console.log('Successfully downloaded file, full path is ' + entry.fullPath)
console.log(entry)
},
function (error) {
console.log('error')
console.log(error)
},
false
)
}

var fileTransfer = new FileTransfer() // eslint-disable-line
var uri = encodeURI('https://MY_SERVER_PATH')
var fileURL = "///storage/emulated/0/Download";
fileTransfer.download(
uri,
fileURL+ 'your_file.pdf',
entry => {
console.log('download complete: ' + entry.toURL())
this.moveFile(entry.toURL())
},
error => {
console.log('download error source ' + error.source)
console.log('download error target ' + error.target)
console.log('download error code' + error.code)
},
false,
{
headers: {
'Authorization': 'Basic asdasdasdasdassdasdasd'
}
}
)
you can directly save downloaded file to your path
Try above code and let me know if its work.

cordova.file.externalApplicationStorageDirectory.
For that path you do not need to request any permission in manifest or require any permission at all.
But for others like external storage and so you need them.
You are #1244 with this problem this year.
Google for runtime permissions.
You can than directly download to the Download directory.

Related

Ionic File Chooser URL issue

I am using this plugin for getting files
https://ionicframework.com/docs/native/file-chooser/
When I select a PDF file from Downloads folder it is giving me a URI like content://com.android.providers.downloads.documents/document/1015
But if i select another file like ZIP or PNG it resolves to a suitable path
content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fwws.zip
content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fopacity.png
How do I get the path from a file in the form of file://….
I have tried the following approaches with no success yet:
FilePath.resolveNativePath
and
normalizeURL
UPDATE:
Imports
import { File } from '#ionic-native/file';
import { FileChooser } from '#ionic-native/file-chooser'
import { FilePath } from '#ionic-native/file-path';
Code for file chooser:
getAndroidFile() {
this.fileChooser.open()
.then(uri => {
console.log("uri", uri);
this.file.resolveLocalFilesystemUrl(uri).then((files) => {
console.log("files", files);
}).catch((error) => { console.log("error", error) });
(<any>window).FilePath.resolveNativePath(uri, (result) => {
this.nativepath = result;
console.log("nativepath", this.nativepath);
}, (err) => {
console.log("err", err);
})
})
}
I have used it as Aaron suggested:
this.file.resolveLocalFilesystemUrl(uri).then((files)=>{
console.log("files",files);
}).catch((error) => { console.log("error", error) });
It still returns as:
filesystem: FileSystem {name: "content", root: DirectoryEntry}
fullPath: "/com.android.providers.downloads.documents/document/1015"
isDirectory: false
isFile: true
name: "1015"
nativeURL: "content://com.android.providers.downloads.documents/document/1015"
Which is the original url.
UPDATE 2:
getBase64Content(nativepath) {
let path = nativepath.substring(0, nativepath.lastIndexOf('/'));
let filename = nativepath.substring(nativepath.lastIndexOf('/'), nativepath.length);
filename = filename.replace("/", "");
console.log("path", path);
console.log("filename", filename);
this.file.readAsDataURL(path, filename)
.then(content => {
console.log('content1', content);
//this will be passed to web API
this.base64content = content;
})
.catch(err => {
console.log("err", err);
})
}
This base64 content is sent overthe to server via api and they convert it back to file.
UPDATE 3:
Sorry for late reply, really busy with another project.
Answers for Koken:
How are you testing this thing?
What command are you using for the execution?
ionic cordova run android
(I check the values by remote debugging using chrome.)
Android emulator? Android device?
Android Device. Redmi note 5 pro(MIUI 10, Android 8.1).
Also... what are the next steps after selecting the file?
This base64 content is sent over to server via api and they convert it back to file.
Hope you guys can help me. With code given. Thanks
you need resolveLocalFilesystemUrl from the FileManager Plugin
https://ionicframework.com/docs/native/file/#resolveLocalFilesystemUrl

Cordova 6.3.1 download PDF and open with FileSystem, FileTransfer and FileOpener2

Im banging my head against my desk because I dont seem to find any answer that works.
I want to download a PDF to the local storage of an android device, and then open it in an external reader as Android is not able to display PDF in the browser. For iOS I simply use the InAppBrowser plugin which works nice btw. Im using cordova 6.3.1.
So, this is my code:
if (cordova.platformId === "android") {
var remoteFile = url;
var localFileName = "tmp.pdf";
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSystem) {
//var fileSystemRoot = cordova.file.dataDirectory; Does not work...
var fileSystemRoot = fileSystem.root.toURL()
console.log(cordova.file.dataDirectory);
var ft = new FileTransfer();
ft.download(remoteFile,
fileSystemRoot + "tmp.pdf", function(entry) {
cordova.plugins.fileOpener2.open(
entry.toURL(),
'application/pdf',
{
error : function(e) {
console.log('Error status: ' + e.status + ' - Error message: ' + e.message + ' - URL: ' + messageObj.url);
},
success : function () {
console.log('file opened successfully');
console.log(fileSystemRoot);
console.log(entry.toURL());
}
}
);
}, function(error) {
console.log("Error in downloading");
console.log(error);
});
}, function(error) {
console.log("Error in requesting filesystem");
console.log(error);
});
}
I have tried tons of different things. fileSystem.root.fullpath, fileSystem.root.toURL(), fileSystem.root.nativeURL but I always end up with a path that does not seem to correspond with the device. I always get the success message that the download worked, adobe reader pops up but says the file is not readable. Not a surprise to me as the path it gives me is something like:
file:///data/data/ch.novalogix.novalib/files/files
That can simply not be true? I searched the whole system for the uploaded file but I dont think its downloaded. I guess I always get a wrong path...
Any ideas?
Thanks in advance!
I used fixed paths instead of the localFileSystem.

How to download base-64 source images in mobile device(Android,iPhone,Windows) and save into device phone memory

I have created one sample app using telerik app builder. I have created one simple view and one js file, render one image in view and convert it into base-64. I have requirement to download this image and save it to device internal storage using javascript
I have used download.js plugin of javascript but download functionality is not working.Please give suggestion.
My Code is below:
function (image)
{
var imageData = image.src;
imageData = imageData.replace('data:image/png;base64,', '');
download(imageData, "image11111", "image/png");
}
I found my own question's answer.
You can download image in mobile device using cordova filetransfer.
function download()
{
var filepath = encodeURI("http://www.telerik.com/sfimages/default-source/logos/app_builder.png"),
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fileSystem) {
fileSystem.root.getFile("sample.jpg", { create: true, exclusive: false }, function (fileEntry) {
// get the full path to the newly created file on the device
var localPath = fileEntry.fullPath;
// massage the path for android devices (not tested)
if (device.platform === "Android" && localPath.indexOf("file://") === 0) {
localPath = localPath.substring(7);
}
// download the remote file and save it
var remoteFile = filepath;
//loadingOverlay.displayLoading("Image will be save on your device.");
var fileTransfer = new FileTransfer();
fileTransfer.download(remoteFile, localPath, function (newFileEntry) {
// successful download, continue to the next image
var dwnldImagePath = newFileEntry.fullPath;
console.log('successful download');
},
function (error) { // error callback for #download
console.log('Error with #download method.', error);
});
});
function(error) { // error callback for #getFile
console.log('Error with #getFile method.', error);
});
})
}

cordova-ionic ngCordova ios or iPhone file read error code 5 ENCODING_ERR

i am using cordova-ionic framework to build app. i am new to the iOS or iPhone
in my requirement, i have to read a file in the app. i am reading file in the android app but same code showing error (code: 5).
i am following code types:
in android:
$cordovaFile.writeFile(( 'user.json', data, {'append':false} )).then(function(result) {
alert('file created.');
alert(JSON.stringify(result));
}, function(err) {
// An error occured. Show a message to the user
alert('file writed');
alert(JSON.stringify(err));
});
i can create file, writing, reading data and removing the file but in ios phone i am not able to create file using the same code.
in iPhone:
var data = {"user":{"name":"errer","email":"sdsdff#gmail.com","username":"sdfsdfsd"}};
$cordovaFile.writeFile(( 'user.json', data, {'append':false} )).then(function(result) {
// Success!
alert('file created.');
alert(JSON.stringify(result));
}, function(err) {
// An error occured. Show a message to the user
alert('file writed');
alert(JSON.stringify(err));
});
i just change my directory is cordova.file.cacheDirecotry/cordova.file.applicationDirectory
$cordovaFile.createFile(( cordova.file.cacheDirecotry+'user.json', true )).then(function(result) {
// Success!
alert('file created.');
alert(JSON.stringify(result));
}, function(err) {
// An error occured. Show a message to the user
alert('file writed');
alert(JSON.stringify(err));
});
all way getting the error like code: 12 or code: 5
please help me to solve this or give me a idea to get application file path
I have some progression.
First, I alert my cordova.file.dataDirectory or cordova.file.documentsDirectory.
They are
file:///var/mobile/...../Library/NoCloud
and
file:///var/mobile/..../Documents
Then I create a File without the prefix and succeed. Referring to this https://github.com/driftyco/ng-cordova/issues/362
and the success message shows that the native url of the file is saved in
file:///var/mobile/...../Library/files
Which is quite strange. By the way, I add the
<preference name="iosPersistentFileLocation" value="Library" />
according to https://github.com/apache/cordova-plugin-file/blob/master/doc/index.md#ios-persistent-storage-location
All the tests are running on IOS, i haven't test for Android.
Updates
All the following code worked for me and give success response
$cordovaFile.checkFile('/test.data')
$cordovaFile.createFile('test.data',false)
$cordovaFile.checkDir('/')
Hope this can solve your problems.
/*
Here is what I am using for my Android and IOS apps
Keep attention to a couple of things:
- Android and IOS have other directorynames for files
- Android devices have different root (myFSRootDirectory1 = Samsung Tab 3, msFSRootDirectory2 = Samsung SII)
- $cordovaFile functions prefixes all pathnames with root
$cordovaFileTransfer functions needs absolute pathnames
Here I create the prefixes for File functions and FileTransfer functions for Android and IOS
*/
// The $ionicPlatform and ionic.Platorm are from Ionic framework
//
$ionicPlatform.ready(function() {
if (ionic.Platform.isAndroid()) {
// If running on Android
console.log('cordova.file.externalDataDirectory: ' + cordova.file.externalDataDirectory);
//
// I use cordova.file.externalDataDirectory because this url is for Android devices
// If you remove the app from the device these url are cleared too on the device. So keep it clean.
// Remove the root from cordova.file.externalDataDirectory
//
myFsRootDirectory1 = 'file:///storage/emulated/0/'; // path for tablet
myFsRootDirectory2 = 'file:///storage/sdcard0/'; // path for phone
fileTransferDir = cordova.file.externalDataDirectory;
if (fileTransferDir.indexOf(myFsRootDirectory1) === 0) {
fileDir = fileTransferDir.replace(myFsRootDirectory1, '');
}
if (fileTransferDir.indexOf(myFsRootDirectory2) === 0) {
fileDir = fileTransferDir.replace(myFsRootDirectory2, '');
}
console.log('Android FILETRANSFERDIR: ' + fileTransferDir);
console.log('Android FILEDIR: ' + fileDir);
}
if (ionic.Platform.isIOS()) {
// if running on IOS
console.log('cordova.file.documentsDirectory: ' + cordova.file.documentsDirectory);
// I use cordova.file.documentsDirectory because this url is for IOS (NOT backed on iCloud) devices
fileTransferDir = cordova.file.documentsDirectory;
fileDir = '';
console.log('IOS FILETRANSFERDIR: ' + fileTransferDir);
console.log('IOS FILEDIR: ' + fileDir);
}
if (ionic.Platform.isAndroid() || ionic.Platform.isIOS()) {
//
// Just functions from the list below one by one ( or chain them)
//
}
});
// Download file from 'http://www.yourdomain.com/test.jpg' to test/one/test.jpg on device Filesystem
var hostPath = 'http://www.yourdomain.com/test.jpg';
var clientPath = fileTransferDir + 'test/one/test.jpg';
var fileTransferOptions = {};
$cordovaFile.downloadFile(hostPath, clientPath, true, fileTransferOptions).then (function() {
});
// Create dir test
$cordovaFile.createDir(fileDir + 'test/').then( function(dirEntry) {
});
// Create dir aganin in dir test
$cordovaFile.createDir(fileDir + 'test/one/').then( function(dirEntry) {
});
// Create empty file test.txt in test/again/
$cordovaFile.createFile(fileDir + 'test/one/test.txt', true).then( function(fileEntry) {
});
// List of files in test/again
$cordovaFile.listDir(fileDir + 'test/one/').then( function(entries) {
console.log('list dir: ', entries);
});
// Write some text into file
$cordovaFile.writeFile(fileDir + 'test/one/test.txt', 'Some text te test filewrite', '').then( function(result) {
});
// Read text written in file
$cordovaFile.readAsText(fileDir + 'test/one/test.txt').then( function(result) {
console.log('readAsText: ', result);
});
Perhaps it's because of a typo? You have cordova.file.cacheDirecotry. Shouldn't that be : cordova.file.cacheDirectory ?
Refer to the original documentation :-
https://github.com/apache/cordova-plugin-file/blob/master/doc/index.md#ios-file-system-layout
iOS has some directories as read-only. Try changing your path.
Let me know if it does not work for you.

How to access external storage with cordova file / file-system-roots plugins?

Problem description:
I can access the internal storage with either the file or the file-system-roots (read and write). But such a file can not be access from an other app. For example if I want to send this file through the emailComposerPlugin, the file can not be accessed by the email client. (Same goes for the "open with" function.)
If I change the options {sandboxed: true} to false (to write to the external storage), it does not work and ends up in a FileUtils.UNKNOWN_ERR. I tried the application while the phone disconnected from USB, as some docs mentioned that external storage can not be accessed while mounted on the pc - same result though.
From what I read on the mailing list this should be possible. It seems I miss a crucial point?
Context:
I try to enable an hybrid application created for iPhone to run on android devices. To have a little playground, I create a small test project.
Edit:
There seems to be a problem between file-system-roots and file plugin. But I have the newest versions of both of them. (File: 1.0.1 File-system-roots: 0.1.0)
Debugging the file-system and file classes show that
private String fullPathForLocalURL(Uri URL) {
if (FILESYSTEM_PROTOCOL.equals(URL.getScheme()) && "localhost".equals(URL.getHost())) {
String path = URL.getPath();
if (URL.getQuery() != null) {
path = path + "?" + URL.getQuery();
}
return path.substring(path.indexOf('/', 1));
// path = "/cache-external" at this point
// results in index out of bounds exception
What have I tried?
config.xml
<preference name="AndroidExtraFilesystems" value="files,files-external,documents,sdcard,cache,cache-external" />
AndroidManifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
javascript code
function createTextDocument(filename, text) {
cordova.filesystem.getDirectoryForPurpose('cache', {sandboxed: false}, successCallback, failureCallback);
function successCallback(directoryEntry){
console.log('directory found (cordova): ' + directoryEntry.toURL());
console.log('directory found (native) : ' + directoryEntry.toNativeURL());
directoryEntry.getFile(filename, {create: true, exclusive: false},
function(fileEntry){
var filePath = fileEntry.toNativeURL();
fileEntry.createWriter(
function(fileWriter){
console.log('start writing to: ' + filePath );
fileWriter.write(text);
console.log('file written');
},failureCallback
);
}, failureCallback
);
}
function failureCallback(error){
console.log('error creating file: ' + error.code);
// results in code 1000
}
}
After digging this whole topic quite a bit, i figured out:
There is no need for the file system roots plugin.
There is more configuration needed in config.xml.
You need to use not the standard FileApi way, but the following to access the files.
JavaScript usage:
window.resolveLocalFileSystemURL(path, cbSuccess, cbFail);
#param path: {string} a cordova path with scheme:
'cdvfile://localhost/<file-system-name>/<path-to-file>'
Examples: 'cdvfile://localhost/sdcard/path/to/global/file'
'cdvfile://localhost/cache/onlyVisibleToTheApp.txt'
#param cbSuccess: {function} a callback method that receives a DirectoryEntry object.
#param cbFail: {function} a callback method that receives a FileError object.
config.xml
<preference name="AndroidPersistentFileLocation" value="Compatibility" />
<preference name="AndroidExtraFilesystems" value="sdcard,cache" />
AndroidManifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
After hours of struggling, finally I'm able to scan sdcard and gain access to the files by providing a directory like "files://pathtofile/".I've submit an API and a sample project
https://github.com/xjxxjx1017/cordova-phonegap-android-sdcard-full-external-storage-access-library
Example of using the API
new ExternalStorageSdcardAccess( fileHandler ).scanPath( "file:///storage/sdcard1/music" );
function fileHandler( fileEntry ) {
console.log( fileEntry.name + " | " + fileEntry.toURL() );
}
The API source code
var ExternalStorageSdcardAccess = function ( _fileHandler, _errorHandler ) {
var errorHandler = _errorHandler || _defultErrorHandler;
var fileHandler = _fileHandler || _defultFileHandler;
var root = "file:///";
return {
scanRoot:scanRoot,
scanPath:scanPath
};
function scanPath( path ) {
window.resolveLocalFileSystemURL(path, _gotFiles, errorHandler );
}
function scanRoot() {
scanPath( root );
}
function _gotFiles(entry) {
// ? Check whether the entry is a file or a directory
if (entry.isFile) {
// * Handle file
fileHandler( entry );
}
else {
// * Scan directory and add media
var dirReader = entry.createReader();
dirReader.readEntries( function(entryList) {
entryList.forEach( function ( entr ) {
_gotFiles( entr );
} );
}, errorHandler );
}
}
function _defultFileHandler(fileEntry){
console.log( "FileEntry: " + fileEntry.name + " | " + fileEntry.fullPath );
}
function _defultErrorHandler(error){
console.log( 'File System Error: ' + error.code );
}
};
Configurations
config.xml
delete preference: preference name="AndroidExtraFilesystems"
make sure testing environment can access it's own external storage at the time of testing. ( e.p. if you connect it with usb, make sure it is connected as a camera; Call the APIs after recieving the "deviceready" event )
Path example:
file:///
file:///somefile/
file:///somefile
file:///somefile/music/aaaaa.mp3

Categories

Resources