I am using the HTTP library to attempt to download a file.
In short, I want to download a file, from a URL, to my device in a place where the user can access it easily.
For simplicity in getting it working the first time I am using the example from the docs.
This is the code I am using:
download (id) {
console.log('Download Started');
getFile("https://raw.githubusercontent.com/NativeScript/NativeScript/master/apps/tests/logo.png").then(function (r) {
console.log(r.path);
}, function (e) {
//// Argument (e) is Error!
});
}
This outputs the path it was saved at as being:
/data/user/0/com.myapp.example/files/logo.png
When I try to go to this location on a file browser from the play store It is nowhere to be found.
Can anyone shed anymore light on this?
Obviously, I want this to work the same on ios and Android but I am testing on Android atm.
Thanks
Note: Preferably going to the downloads folder
Once you got you file you should save it locally to the desired directory. In your case with Downloads folder you can do the following:
// grabs the path for Downloads (string value)
var androidDownloadsPath = android.os.Environment.getExternalStoragePublicDirectory(
android.os.Environment.DIRECTORY_DOWNLOADS).toString();
// creates PATH for folder called MyFolder in /Downloads (string value)
var myFolderPath = fileSystem.path.join(androidDownloadsPath, "MyFolder");
// creates the folder itself in Downloads/MyFolder
var folder = fileSystem.Folder.fromPath(myFolderPath);
// creates a path of kind ../Downloads/MyFolder/my-file-name.jpg
var path = fileSystem.path.join(cosmosFolderPath, fileName);
var exists = fileSystem.File.exists(path);
if (!exists) {
// finally saves the file to to created path with the passed extension - saved is of kind boolean
var saved = res.saveToFile(path, enums.ImageFormat.jpeg);
}
Based on this example app here
@NickIliev This doesn't work with the file type. that is an example using image source. I believe this in invalid here. The file type which the nativescript getFile method exposes doesn't have a saveToFile method. Also, this might not be just an image. It could be anyfile. Excel, Word, Image, PDF etc.
On another note, I believe the app you pointed to uses Nativescript not nativescript angular which Im not sure can take advantage of android.os.Environment methods.
You should be able to get at everything like that. Its still NativeScripts environment after all. Using TypeScript may throw errors if you are missing the Typings, which is fairly easily overcome whether they exist or not.
Have you given it a go?
@RyanSMurphy perhaps you can create/save a file using the file-system and its class File. You can write text or binary data with the methods provided. Some example here
If you want to implement some native logic you can use the native APIs (both iOS and Android )in NativeScript+Angular-2 application but as @matt4446 noticed you will need some TypeScript declarations. You can find them here but notice that for Angular project you should also cast some additional DOM related properties like done here
So if I am correct, there is no easy way and definite 100% working way that anyone can tell me of downloading files to the device in an easily accessible location by the user?
Hey @RyanSMurphy quite the opposite,!The method getFile has optional parameter which is destinationDownloadPath
getFile
e.g. with destinationPath
var documents = fs.knownFolders.documents();
var path = fs.path.join(documents.path, "FILE.txt");
http.getFile("http://httpbin.org/robots.txt", path).then(res => {
var savedFile = fs.File.fromPath(path);
savedFile.readText().then(content => {
console.log(content);
})
console.log(savedFile.path);
})
Or alternatively, you can save text or binary content accessed from a remote location
.e.g. with saving content
import * as http from "http";
import * as fs from "file-system"
....
http.getFile("http://httpbin.org/robots.txt").then(downloadedFile => {
var documents = fs.knownFolders.documents();
var path = fs.path.join(documents.path, "FILE.txt");
mySavedFile = fs.File.fromPath(path);
// if your file content is not text you can use readSync and WriteSync for binary data
downloadedFile .readText().then(content => {
mySavedFile.writeText(content).then(() => {
console.log("Succsess!");
mySavedFile.readText().then(content => {
console.log("saved content: " + content);
})
})
})
})
@NickIliev - I think this is a good candidate for the code samples?
@NickIliev
Thanks for coming back to me.
Unfortunately, the examples you posted are both invalid and only strengthen my point that there is currently no way on Nativescript / Angular to save a file to a device in a user accessible location.
The examples are invalid because the premise of them is this method:
fs.knownFolders.documents();
This method does NOT save the file to a user accessible location. It saves it to a location that neither users or external applications can access. We need a way to save them to a user accessible location,
So as no one has been able to prove otherwise I am right in saying in Nativescript angular there is currently no easy way/way at all to save a file in a user accessible location on a device? Which is a HUGE limitation.
@valentinstoychev
This information is relatively simple to come across with a google search and doesn't address the larger issue and therefore I wouldn't suggest is a good addition in regards to what is already there.
@matt4446 How would you suggest this be done?
@RyanSMurphy I see your point and indeed the example above is valid for the in-app folders which are not accessible from "outside" and you want the file to be stored in user accessible folder a,k,a, some of the external storage folders (e.g. Download, or custom folders in the SD cards). Indeed this can not be achieved our-of-the-box however, you can use the native API to get access to public folders.
Note that for Android 6.x and above runtime permissions are required as well (setting WRITE_EXTERNAL_STORAGE from AndroidManifest is not enough you will need to require those permissions runtime)
Here is an example of how to save robots.txt text file in custom folder created in the public Download folder (for Android Nexus 5x in Storate>>Explore>>Download>>customFolder but this path will be different depending on the device and if it is using SD card or not)
We need runtime permissions for Andoird API23+
tns plugin add nativescript-permissions
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
permissions.requestPermission([
"android.permission.INTERNET",
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE",
], "I need these permissions")
.then(function (res) {
console.log("Permissions granted!");
})
.catch(function () {
console.log("No permissions - plan B time!");
});
}
Then creating the file in the user accessible folder Download/customFolder and saving the file robots.txt with the content get from the http request.
var file: fs.File;
var fileName = "robots.txt";
var androidDownloadsPath = android.os.Environment.getExternalStoragePublicDirectory(android.os.Environment.DIRECTORY_DOWNLOADS).toString();
var customFolderPath = fs.path.join(androidDownloadsPath, "customFolder");
var folder = fs.Folder.fromPath(customFolderPath );
var path = fs.path.join(customFolderPath , fileName);
var exists = fs.File.exists(path);
file = fs.File.fromPath(path);
httpModule.getFile("http://httpbin.org/robots.txt").then(res => {
console.log(res);
var content = res.readTextSync(e=> { console.log(e); });
return content;
}).then(cn => {
file.writeText(cn.toString()).then(() => {
console.log("content saved!");
})
})
@RyanSMurphy based on the same code as the one above but using binary data with readSync and writeSync you can save any kind of file not only text
@NickIliev Sorry about late response. Thanks for your detailed reply.
I am having trouble using the line:
android.os.Environment.getExternalStoragePublicDirectory(android.os.Environment.DIRECTORY_DOWNLOADS).toString();
Do you know how to resolve this on nativescript-angular?
Also, do you know of a solution to iOS since i think that only works for android?
Thanks
@RyanSMurphy, in order to access the native APIS, thought any TypeScript enabled project you will need the TypeScript declaration files for the native APIs. The good news is that you don't need to generate those files as we are providing them with tns-platform-declarations package. However to enable them under Angular-2 project requires some additional steps as follows:
npm i tns-platform-declarations --saveDev
then open tsconfig.json and add the _lib_ option
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"experimentalDecorators": true,
"lib": [
"es2016"
]
}
}
and finally open _references.d.ts_ and modify its tcontent to look like this
/// <reference path="./node_modules/tns-core-modules/tns-core-modules.es2016.d.ts" />
/// <reference path="./node_modules/tns-platform-declarations/ios.d.ts" />
/// <reference path="./node_modules/tns-platform-declarations/android.d.ts" />
declare type Comment = any;
declare type CloseEvent = any;
declare type Document = any;
declare type DocumentFragment = any;
declare type Element = any;
declare type History = any;
declare type HTMLAnchorElement = any;
declare type HTMLCollection = any;
declare type HTMLDocument = any;
declare type HTMLElement = any;
declare type HTMLInputElement = any;
declare type HTMLScriptElement = any;
declare type HTMLStyleElement = any;
declare type KeyboardEvent = any;
declare type Location = any;
declare type MessageEvent = any;
declare type MouseEvent = any;
declare type Node = any;
declare type NodeList = any;
declare type Text = any;
declare type WebSocket = any;
For reference, you can take a look at how these are set in this application.
Confirmed.
This solution will download the file to a user accessible location on Android, albeit the user will probably need to have a file manager installed, so probably not great for the average user. iOS solution still to be discovered, although will follow the same lines. It might be useful to document the last comment about using native apis for Angular.
Thanks
Hi @NickIliev ,
I am trying write some data to a css file (It is present in my app directory) from a map object (code snippet is below), But I am getting "EXCEPTION: Uncaught (in promise): Error: java.io.IOException: open failed: EROFS (Read-only file system)" error with the following approach.
import * as fs from "file-system";
file: fs.File;
this.file = fs.File.fromPath("../mapData.css");
this.mapObject.forEach(function(value,key,map){
console.log("Key: "+ key + "\t" + "Value: "+ value);
this.file.writeText(key + ":" + value).then(() => {
console.log("Success!");
});
});
Please let me how to fix this error, and how can I achieve this functionality( I am assuming that I am accessing the css file in a wrong way here), I am new to native-script so please pardon my ignorance.
What I was able to do is use a full path to the folder I want to save to on Android.
Here's relevant piece of code:
import { knownFolders, File, Folder } from "file-system";
// then below in the component...
export class DocsComponent implements OnInit {
public folder: Folder;
downloadFile(file: any) {
let basePath : Folder;
basePath = Folder.fromPath('/sdcard/Download');
this.folder = basePath;
this.file = this.folder.getFile(file.name + file.extension);
this.jobsService.getFile(file.rowGUID).subscribe(fileData => {
let imagedata = fileData;
let rawdata = imagedata.rawData;
this.file.writeText(rawdata || "some random content")
.then(result => {
console.log(result);
this.file.readText()
.then(res => {
this.successMessage = "Successfully saved in " + this.file.path;
this.writtenContent = res;
this.isItemVisible = true;
console.log(this.successMessage);
});
}).catch(err => {
console.log(err);
});
});
}
}
Please disregard the jobsService.getFile which is a wrapper for http.get in my app.
Note I saved the file in /sdcard/Download folder on my Genimotion emulator.
I needed to change app permissions beforehand and allow read/write access to storage in the emulator.
@NickIliev,
As you mentioned in this comment,
based on the same code as the one above but using binary data with readSync and writeSync you can save any kind of file not only text.
Now come to my point,
I want to get the zip file nearly 50-100 mb's from the api server in http post call with passing some datas.
Note - My response is Base64 format compressed zip file.
If i use getFile method means,
1.how i will pass datas(params) with call?
I am working on Nativescript with Angular application.
@MilosStanic @matt4446 @valentinstoychev @RyanSMurphy Please suggest the way for achieve it!
@NickIliev
How to use http getFile function with nativescript angular?
I have looked in NS Angular http class, but its not having the getFile funtion.
Most helpful comment
@NickIliev
How to use http getFile function with nativescript angular?
I have looked in NS Angular http class, but its not having the getFile funtion.