How to catch errors when downloading a file - android

Im using react-native-fs to download files from a server and to read the local system. Everything works great, however Im having an issue where I don't know how to catch the failure when downloading a file.
For example, if the user lost network, how can I catch that? What I want is to show an alert message to the user, hide the download percentage message that I'm showing and delete the uncomplete download.
I have the following code, but the catch never runs:
const result = FS.downloadFile({
fromUrl: url, // URL to download file from
toFile: `${CACHE_DIR}/${name}`, // Local filesystem path to save the file to
background: false,
progressDivider: steps,
begin: onBegin,
progress: onProgress,
readTimeout: 2 * MIN,
connectionTimeout: 30 * SEC,
});
return result.promise
.then(() => {
this.index[name] = {
name,
path: `${CACHE_DIR}/${name}`,
size: 0,
};
return this.index[name];
})
.catch((error) => {
console.log('error!', error); // <-- This code never runs :(
// Show and alert message to the user...
// Hide downloading message
// Delete incomplete download file
});
For now I'm only focusing on Android, but later on I will move on to iOS. I wonder if the same issue happens on iOS as well or if is only on Android.
Thank you so much for your help.

You can use the second callback of then
return result.promise
.then(() => {
this.index[name] = {
name,
path: `${CACHE_DIR}/${name}`,
size: 0,
};
return this.index[name];
}, (error) => {
console.log('error!', error); // <-- This code never runs :(
// Show and alert message to the user...
// Hide downloading message
// Delete incomplete download file
});

Related

Pasre server on AWS EC2 giving 141 error on cloud code

I am now using the below cloud code to only update "downloads" column on my parse server running on AWS EC2 instance. But I am getting the error code 141(invalid function)
Parse.Cloud.define("updateDownloads", async (request) => {
const query = new Parse.Query(request.params.className);
query.get(request.params.objectId)
.then((watchFace) => {
downloads = watchFace.get("downloads")
watchFace.set("downloads", downloads + 1);
await watchFace.save(null, { useMasterKey: true });
return "download updated";
}, (error) => {
return "something went wrong";
});
});
I have place my code in /opt/bitnami/cloud/main.js.
I even tried adding “cloud”: “/opt/bitnami/cloud/main.js” in config.json file but then the parse server gives 503 Service Unavailable error. So I removed it.
If you don't add the cloud code main.js file to your parse server configuration, parse server will never find your function, and that's why you get the invalid function error.
If you get error when adding the file, you are either adding it in a wrong way (you need to check your parse server initialization code) or the config.json is in wrong format or the cloud code has a problem.
The best way to figure it out is by checking your logs.
At a first glance, a problem that I see (may have others) is the usage of await in a function that is not async. You are also using a combination of async and then, which is little strange.
I'd recommend you to change the code to something like:
Parse.Cloud.define("updateDownloads", async (request) => {
const query = new Parse.Query(request.params.className);
const watchFace = await query.get(request.params.objectId);
const downloads = watchFace.get("downloads");
watchFace.set("downloads", downloads + 1); // You can use inc function to avoid concurrency problem
await watchFace.save(null, { useMasterKey: true });
return "download updated";
});

Uploading chunked file using Ionic4 and Angular HttpClient fails after some successful uploads with net::ERR_FILE_NOT_FOUND

i'm trying to upload a large file (500+Mb, but could be even bigger) to our php server, using an app written in Ionic4+Angular+Cordova, on an emulator with Android 10.
I set up a system to upload the file in chunks.
It reads the file choosen by the user using THIS PLUGIN, chunk by chunk (5Mb per chunk).
Then it proceeds to send it to our server performing a POST request with Content-type multipart/form-data.
The file goes to server, server saves it, says "OK", then the app proceeds to send the following chunk.
Everything works fine, for the first 25/29 chunks.
Then, the POST request fails with
POST http://192.168.1.2/work/path/to/webservices/uploadChunks.php net::ERR_FILE_NOT_FOUND
I tried:
starting at another point in the file instead of byte 0 - got the same error
reading the file chunk by chunk, without making any POST request- could cycle the whole 500Mb file
reading the file chunk by chunk and making the POST requests, but not sending the chunks with them - could execute every single call without any error, through the end of the file
reading the file chunk by chunk and sending them to ANOTHER webservice - got the same error
reading the file chunk by chunk and performing a POST request to another webservice, with content-type application/json and putting the formData object into the request body (not sure this is a valid test tho) - could execute every single call without any error, through the end of the file
Checking out memory snapshots taken in chrome inspector during different chunks upload did not show any sign of memory leak.
The case was tested on a rather old device, where the same procedure caused the app to exit, without signaling any error (not even in logcat apparently).
Here is the piece of code used to chunk and send the file:
const generatedName = 'some_name_for_file';
// Path obtained from fileChooser plugin
let path_to_file = 'content://com.android.externalstorage.documents/document/primary%3ADownload%2Ffilename.avi'
const min_chunk_size = (5 * 1024 * 1024);
// Converting path to file:// path
this.filePath.resolveNativePath(path_to_file).then((resolvedPath) => {
return this.fileAPI.resolveLocalFilesystemUrl(resolvedPath);
}, (error) => {
console.log('ERROR FILEPATH');
console.log(error);
return Promise.reject('Can not access file.<br>Code : F - ' + error);
}).then(
(entry) => {
path_to_file = entry.toURL();
console.log(path_to_file);
(entry as FileEntry).file((file) => {
//Getting back to the zone
this.ngZone.run(() => {
// Re-computing chunk size to be sure we do not get more than 10k chunks (very remote case)
let file_chunk_size = file.size / 10000;
if (file_chunk_size < min_chunk_size) {
file_chunk_size = min_chunk_size;
}
//Total number of chunks
const tot_chunk = Math.ceil(file.size / file_chunk_size);
const reader = new FileReader();
let retry_count = 0; //Counter to check on retries
const readFile = (nr_part: number, part_start: number, length: number) => {
// Computing end of chunk
const part_end = Math.min(part_start + length, file.size);
// Slicing file to get desired chunk
const blob = file.slice(part_start, part_end);
reader.onload = (event: any) => {
if (event.target.readyState === FileReader.DONE) {
let formData = new FormData();
//Creating blob
let fileBlob = new Blob([reader.result], {
type: file.type
});
formData.append('file', fileBlob, generatedName || file.name);
formData.append('tot_chunk', tot_chunk.toString());
formData.append('nr_chunk', nr_part.toString());
// UPLOAD
const sub = this.http.post('http://192.168.1.2/path/to/webservice/uploadChunk.php', formData).subscribe({
next: (response: any) => {
console.log('UPLOAD completed');
console.log(response);
retry_count = 0;
if (response && response.status === 'OK') {
//Emptying form and blob to be sure memory is clean
formData = null;
fileBlob = null;
// Checking if this was the last chunk
if (part_end >= file.size) {
// END
callback({
status: 'OK'
});
} else {
// Go to next chunk
readFile(nr_part + 1, part_end, length);
}
//Clearing post call subscription
sub.unsubscribe();
} else {
//There was an error server-side
callback(response);
}
},
error: (err) => {
console.log('POST CALL ERROR');
console.log(err);
if (retry_count < 5) {
setTimeout(() => {
retry_count++;
console.log('RETRY (' + (retry_count + 1) + ')');
readFile(nr_part, part_start, length);
}, 1000);
} else {
console.log('STOP RETRYING');
callback({status:'ERROR'});
}
}
});
}
};
//If for some reason the start point is after the end point, we exit with success...
if (part_start < part_end) {
reader.readAsArrayBuffer(blob);
} else {
callback({
status: 'OK'
});
}
};
//Start reading chunks
readFile(1, 0, file_chunk_size);
});
}, (error) => {
console.log('DEBUG - ERROR 3 ');
console.log(error);
callback({
status: 'ERROR',
code: error.code,
message: 'Can not read file<br>(Code: 3-' + error.code + ')'
});
});
}, (error) => {
console.log('ERROR 3');
console.log(error);
return Promise.reject('Can not access file.<br>Code : 3 - ' + error);
}
);
I can not figure out what is going wrong. Can someone help me debug this, or knows what could be going on?
Thank you very much.
I still do not know what caused this issue, but i resolved using a PUT request instead of a POST request, sending the raw chunk, and putting additional data in custom headers (something like "X-nr-chunk" or "X-tot-chunk"). Upload completed fine without the error message.
I also used the cordova-advanced-http plugin, but i do not think it made a difference here, since it did not work with the POST request, like the other method (httpClient).
This has been tested on android only for now, not on iOS. I'll report if there is any problem. For now i consider this solved, but if you know what may have caused this problem, please share your thoughts.
Thanks everyone.

Copying camera image in phonegap/cordova not working

I am trying to save images i take with the camera on my phonegap app to a more permanent destination. I run the app on iOS and Android so i need this to work on ios and android.
So far i can get the image file name and temporary storage path.
I can also get the base file system path for the device i am running (currently an Android Galaxy S7 edge).
However, i am unable to move/copy the file from the temporary storage to the new one as it fails.
Ive got the following code and ive got a try/catch around the code which moves the images. in the catch i have an alert which triggers always as there is something wrong with my code.
Ive tried changing the moveTo to CopyTo and changing the code from output.moveTo to things like cordova.file.moveTo without success.
If anyone could help i would be greatful.
Thanks Kind regards
const tempFilename = imageURI.substr(imageURI.lastIndexOf('/') + 1);
const tempBaseFilesystemPath = imageURI.substr(0, imageURI.lastIndexOf('/') + 1);
const newBaseFilesystemPath = cordova.file.dataDirectory;
var output=tempBaseFilesystemPath+tempFilename;
var saveOutput=newBaseFilesystemPath+tempFilename;
alert(tempFilename+","+tempBaseFilesystemPath+","+newBaseFilesystemPath+","+tempFilename);
try{
output.moveTo(tempBaseFilesystemPath, tempFilename, newBaseFilesystemPath,tempFilename)
.then(function (success) {
alert("done");
}, function (error) {
alert("not done");
});
}
catch{
alert("didnt run");
}
const storedPhoto = output;
localStorage.setItem('imageLoc',storedPhoto);
localStorage.setItem('goodPic','not');
},
function( message ) {
},
{
quality: 30,
destinationType: Camera.DestinationType.FILE_URI,
});
}

React Native Background Download

I am about to build an app which locally stores multiple MySqlite *.db files. These are used to populate the app with data.
The app should regularly check if there is a newer version for one of these files and, if so, download it and replace the old version of the file.
This update process should happen in the background, while the app is inactive or even closed.
I have seen several plugins, which can be used to execute tasks in the Background (like https://www.npmjs.com/package/react-native-background-task). This could be used to check for updates regularly, but the time limit of 30s on iOS will probably not be enough to download the *.db file. Also, the plugin forces a minimum Android API Version of 21.
My question is: Is it possible to poll for updates in the background AND download them, replacing old files?
I found some useful plugins.
1. react-native-fetch-blob
https://github.com/wkh237/react-native-fetch-blob/wiki/Classes#rnfetchblobconfig
It has IOSBackgroundTask option.
RNFetchBlob
.config({
path : dest_file_path,
IOSBackgroundTask: true,
overwrite: true,
indicator: true,
})
.fetch('GET', download_url, {
//some headers ..
})
.progress( (received, total) => {
console.log('progress : '+ received + ' / ' + total);
})
.then((res) => {
console.log('# The file saved to :', file_path);
})
By the way, it looks doesn't work properly.
Not sure if I miss something...
2. react-native-fs
https://github.com/itinance/react-native-fs#downloadfileoptions-downloadfileoptions--jobid-number-promise-promisedownloadresult-
const ret = RNFS.downloadFile({
fromUrl: download_url,
toFile: dest_file_path,
connectionTimeout: 1000 * 10,
background: true,
discretionary: true,
progressDivider: 1,
resumable: (res) => {
console.log("# resumable :", res);
},
begin: (res) => {
// start event
},
progress: (data) => {
const percentage = ((100 * data.bytesWritten) / data.contentLength) | 0;
console.log("# percentage :", percentage);
},
});
jobId = ret.jobId;
ret.promise.then((res) => {
console.log('Download finished.');
RNFS.completeHandlerIOS(jobId);
jobId = -1;
}).catch(err => {
console.log('error');
jobId = -1;
});
It looks working well.
By the way, when I try download in background via push notification, it doesn't start download unless I open the app.
Anyone can solve this problem?
For download in the background, the best module I have used was react-native-background-downloader, which have pause, resume and the download percentage.
import RNBackgroundDownloader from 'react-native-background-downloader';
let task = RNBackgroundDownloader.download({
id: 'dbfile',
url: 'https://link-to-db/MySqlite.db'
destination: `${RNBackgroundDownloader.directories.documents}/MySqlite.db`
}).begin((expectedBytes) => {
console.log(`Going to download ${expectedBytes} bytes!`);
}).progress((percent) => {
console.log(`Downloaded: ${percent * 100}%`);
}).done(() => {
console.log('Download is done!');
}).error((error) => {
console.log('Download canceled due to error: ', error);
});
// Pause the task
task.pause();
// Resume after pause
task.resume();
// Cancel the task
task.stop();
This module is also suitable for iOS and you can download multiple files in a row in the background.

Cannot open files with FileOpener2, but not getting an error in Android

I am attempting to open a PDF file with FileOpener2 (through ng-cordova) with the following code:
$cordovaFile.checkFile(cordova.file.dataDirectory, attachmentPath)
.then((fileEntry) => {
// success
fileEntry.getMetadata((metadata) => {
// metadata.size is in bytes
var megabyteSize = metadata.size / 1048576;
if (megabyteSize > 5) {
var path = cordova.file.dataDirectory + attachmentPath;
console.log(path); // prints: file:///data/data/com.ionicframework.enhatch146189/files/attachments/CS-353ES_CS-420ES_Eng.pdf which is correct
$cordovaFileOpener2.open(path, 'application/pdf').then(() => {
console.log("Opened!") // prints
}, (error) => {
console.log(error);
usePDFJs(); // tries to render PDF in app with PDFJs
});
} else {
usePDFJs();
}
})
}, function (error) {
// error
console.error(error);
});
What happens confuses me: it prompts me with an "open this file in Adobe Reader?" and lists the other PDF viewers, and the console prints "Opened!"
However, no matter what I open ANY pdf in, I get some sort of error such as "cannot open this PDF file".
Can anyone see something wrong with this code?
Apparently, if you use cordova.file.dataDirectory on android you can't open those files in other applications or attach them to emails. Silly mistake -- coded too fast and read too little on the documentation. Using cordova.file.externalApplicationStorageDirectory solved the issue.

Categories

Resources