Downloading a file from expo react-native problem - react-native

So in the following snipet everything works fine, problem is the FakeVaultDownloads folder is created inside the pictures folder in the devices storage. Is there something else I can use to create this folder inside the downloads folder? Issues is I am also downloading other file types like pdfs and txt documents and I don’t want those inside the pictures folder it doesn’t make sense those to be there, so I am trying to save them to a universal folder and not inside the pictures folder. Thanks for reading and sorry if I posted this in the wrong category any help is greatly appreciated.
let downloadedFile = await FileSystem.downloadAsync(res.data.url, `${FileSystem.documentDirectory}${res.data.name}`);
MediaLibrary.requestPermissionsAsync().then( async (res) => {
const asset = await MediaLibrary.createAssetAsync(downloadedFile.uri);
const album = await MediaLibrary.getAlbumAsync('Download');
if (album == null) {
await MediaLibrary.createAlbumAsync('Download', asset, false);
alert("File was downloaded to the Download folder ")
} else {
await MediaLibrary.addAssetsToAlbumAsync([asset], album, false);
alert("File was downloaded to the Download folder ")
}
}).catch(err => console.log("Error: ", err));
})
.catch((err) => {
console.log("Failed" , err);
alert("Response failed something is up with the server")

Related

Save image from expo assets to user device but first let the user select the target folder

I have researched a lot , even used ChatGTP3 solutions ( which by the way are cool ) but nothing works .
So i will explain my case ( i am targeting android ) :
Inside the local expo assets i have 30 images
i want to allow the user to download this images to his device for whatever reason so i have provided a download button :
When you press the download button i want the user to be able to pick the folder where he wants to save the image and then save it that's it here is an example of many codes i have tried it just doesn't work :
import * as FileSystem from 'expo-file-system'
const saveToPhone = async () => {
try {
// Assuming that you have a file called '30.jpg' in your Expo assets folder
const fileUri = Asset.fromModule(require('../../../assets/images/motivational/30.jpg')).uri
// Let the user pick a folder to save the file to
const saveDir = await FileSystem.getDocumentDirectoryAsync()
// Create a new file in the save directory with the same name as the file in assets
const savePath = `${saveDir}/imageName.jpg`
// Copy the file from assets to the save directory
await FileSystem.copyAsync({ from: fileUri, to: savePath })
} catch (error) {
console.log(error)
}
}
I have tried so many codes for days i can't find out how to do that .I am using latest expo sdk 47 .
Update 24/12/2022
After using #proto answer:
import * as FileSystem from 'expo-file-system'
import * as DocumentPicker from 'expo-document-picker'
const saveToPhone = async () => {
try {
// Assuming that you have a file called '30.jpg' in your Expo assets folder
const fileUri = Asset.fromModule(require('../../../assets/images/motivational/30.jpg')).uri
// Let the user pick a folder to save the file to
const saveDir = await DocumentPicker.getDocumentAsync()
// Create a new file in the save directory with the same name as the file in assets
const savePath = `${saveDir.uri}/imageName.jpg`
// Copy the file from assets to the save directory
await FileSystem.copyAsync({ from: fileUri, to: savePath })
} catch (error) {
console.log(error)
}
}
I am getting the following error
LOG [Error: Location 'http://192.168.1.13:8081/assets/assets/images/motivational/30.jpg?platform=android&hash=0cf4bfe9c8ccb915c1d43a641d662544?platform=android&dev=true&hot=false' isn't readable.]
You need an additional module called DocumentPicker from expo-document-picker. Instead of getting directory directly, try selecting a document and extract its path.
Something like this :
import * as DocumentPicker from 'expo-document-picker'
const saveDir = await DocumentPicker.getDocumentAsync()
const savePath = `${saveDir.uri}/imageName.jpg`
So now the code should look like this :
import * as FileSystem from 'expo-file-system'
import * as DocumentPicker from 'expo-document-picker'
const saveToPhone = async () => {
try {
// Assuming that you have a file called '30.jpg' in your Expo assets folder
const fileUri = Asset.fromModule(require('../../../assets/images/motivational/30.jpg')).uri
// Let the user pick a folder to save the file to
const saveDir = await DocumentPicker.getDocumentAsync()
// Create a new file in the save directory with the same name as the file in assets
const savePath = `${saveDir.uri}/imageName.jpg`
// Copy the file from assets to the save directory
await FileSystem.copyAsync({ from: fileUri, to: savePath })
} catch (error) {
console.log(error)
}
}

How to create a .pdf or .csv file automatically in React Native for Android 11 (or later)

The following code works up to Android 10, it is creating a csv file in the DCIM folder:
import * as FileSystem from 'expo-file-system';
import * as MediaLibrary from 'expo-media-library';
export async function saveCSV() {
const permission = await MediaLibrary.requestPermissionsAsync();
if (permission.status != 'granted') {
console.log("Permission not Granted!")
return;
}
// CSVLocation
const directoryUri = FileSystem.documentDirectory;
const fileUri = directoryUri + `formData.csv`;
// Save to DCIM folder
const asset = await MediaLibrary.createAssetAsync(fileUri);
try {
const album = await MediaLibrary.getAlbumAsync('album');
if (album == null) {
console.log("ASSET", asset)
await MediaLibrary.createAlbumAsync('album', asset, true);
} else {
await MediaLibrary.addAssetsToAlbumAsync([asset], album, true)
.then(() => {
console.log('File Saved Successfully!');
})
.catch((err: string) => {
console.log('Error In Saving File!', err);
});
}
} catch (e) {
console.log(e);
}
}
Previously this line of code was executed in another function to create a file in the fileUri used above:
await FileSystem.writeAsStringAsync(fileUri, CSVheader + newInfo);
This issue has been described here: https://github.com/expo/expo/issues/12060
In short: Expo Media library is able to save image/video/audio assets so it will fail with other file types. Weirdly enough it was working fine with .pdf and .csv up to Android 10.
In the link above, and also on stackoverflow there are solutions using StorageAccessFramework. However, the user needs to create a subdirectory inside Downloads every time a file needs to be saved. I would like to make it automatically without any popups (after permission is granted).
The destination folder doesn't matter as long as it is accessible by the user later.

How to use moveFile react-native-fs

i need share local image on react native app, use react-native share and react-native-fs. Image is in local folder in root app named 'images'. How to share this image. Do i need copy or move image to temp and use that image for getting absolute path.
This is my code. rnfs.movefile don't work
getAssetFileAbsolutePath = async () => {
const dest =
`${RNFS.TemporaryDirectoryPath}${Math.random().toString(36)
.substring(
.7)}.png`;
const img = './images/page1.png';
try {
await RNFS.moveFile(img, dest);
console.log("dobro", dest)
} catch(err) {
console.log("greska", err)
}
}
I get error “page1.png” couldn’t be moved to “tmp” because either the former doesn’t exist, or the folder containing the latter doesn’t exist.
use rn-fetch blob with react native share
first find path of the image and convert it to base64.
then share image use react native share package
ShareFile(file) {
let imagePath = null;
RNFetchBlob.config({
fileCache: true
})
.fetch("GET", file)
// the image is now dowloaded to device's storage
.then(resp => {
// the image path you can use it directly with Image component
imagePath = resp.path();
return resp.readFile("base64");
})
.then(async base64Data => {
var base64Data = `data:image/png;base64,` + base64Data;
// here's base64 encoded image
await Share.open({ url: base64Data });
// remove the file from storage
return fs.unlink(imagePath);
});
}

React Native RNFetchBlob getting the URI of the file after download

I am developing a React Native project. What I am trying to do now is that I am downloading and saving the downloaded file to the device. I am using this package, https://www.npmjs.com/package/rn-fetch-blob for downloading the file.
This is my code
RNFetchBlob.config({
fileCache: true,
})
.fetch('GET', 'https://static.standard.co.uk/s3fs-public/thumbnails/image/2016/05/22/11/davidbeckham.jpg?w968', {
})
.then((res) => {
Alert.alert(res.path());
})
After download, res.path returns the path like this.
I am trying to convert it to the URI to be displayed using the Image component. I tried binding the following state object to the Image component.
{
uri: res.path()
}
It did not work. That is why as a next attempt, I am trying to convert the path into URI and display the uri. How can I do that?
Try providing the path of the file where you want to download it,
const directoryFile = RNFS.ExternalStorageDirectoryPath + "/FolderName/";
RNFS.mkdir(directoryFile);
const urlDownload = input.url;
let fileName;
fileName = "filename.zip";
let dirs = directoryFile + fileName;
RNFetchBlob.config({
// response data will be saved to this path if it has access right.
path: dirs
}).fetch("GET", urlDownload, {
//some headers ..
})
.then(res => {
console.log("The file saved to ", res.path());
//RNFS.moveFile(dirs, directoryFile + fileName); // -> uncomment this line if it still does not store at your desired path
alert("File Downloaded At Folder");
})
.catch(err => {
console.log("Error: " + err);
});

Download using jsPDF on a mobile devices

I have a page that includes a download button using jsPDF. On desktop machines it downloads the page as it should. However, pdf.save() does not work on my tablet or phone.
I tried to add a special case for mobile devices to open the PDF in a new window, since mobile devices don't download things the same as desktops, with the idea being that once the PDF is open in a new window the user can choose to save it manually.
var pdf = new jsPDF('p', 'pt', 'letter');
var specialElementHandlers = {
'#editor': function (element, renderer) {
return true;
}
};
html2canvas($("#pdf-area"), {
onrendered: function (canvas) {
$("#pdf-canvas").append(canvas);
$("#pdf-canvas canvas").css("padding", "20px");
}
});
var options = {
pagesplit: true
};
function download(doctitle) {
pdf.addHTML($("#pdf-area")[0], options, function () {
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
pdf.output('dataurlnewwindow');
} else {
pdf.save(doctitle);
}
});
}
But the download function still does nothing on my tablet/phone. I tested it with this to make sure the pdf.output() function was working:
pdf.addHTML($("#pdf-area")[0], options, function () {
pdf.output('dataurlnewwindow');
});
and it does still work on desktop, but does nothing on mobile.
jsPDF won't download files on mobile apps by this pdf.save(). I have tried and searched for this issue but could not find a complete solution in one place. I am using the file and file-opener plugin. I have developed the solution in Ionic React. Install below modules.
npm i jspdf
npm install cordova-plugin-file
npm install #ionic-native/file
npm install cordova-plugin-file-opener2
npm install #ionic-native/file-opener
ionic cap sync
Go to your file and add these import statements.
import { jsPDF } from "jspdf";
import 'jspdf-autotable';
import { FileOpener } from '#ionic-native/file-opener;
import { File } from '#ionic-native/file';
import { isPlatform } from "#ionic/react";
Check the pdfDownload function
const pdfDownload = async () => {
var doc = new jsPDF();
doc.setFontSize(40);
doc.text("Example jsPDF", 35, 25)
let pdfOutput = doc.output();
if (isPlatform("android")) {
// for Android device
const directory = File.externalRootDirectory + 'Download/';
const fileName = `invoice-${new Date().getTime()}.pdf`
File.writeFile(directory, fileName, pdfOutput, true).then(succ => {
FileOpener.showOpenWithDialog(directory + fileName, 'application/pdf')
.then(() => console.log('File opened'))
.catch(error => console.log('Error opening file', error));
},
err => {
console.log(" writing File error : ", err)
})
} else if (isPlatform("ios")) {
// for iOS device
console.log('ios device')
const directory = File.tempDirectory;
const fileName = `invoice-${new Date().getTime()}.pdf`
File.writeFile(directory, fileName, pdfOutput, true).then(success => {
FileOpener.showOpenWithDialog(directory + fileName, 'application/pdf')
.then(() => console.log('File opened'))
.catch(e => console.log('Error opening file', e));
},
err => {
console.log(" writing File error : ", err)
})
} else {
// for desktop
doc.save("invoice.pdf");
}
}
I had similar issue.
jsPDF won't download file on phones/ tablets / ipads using "pdf.save()".
Do it through File plugin if you are using cordova/phonegap, this will save pdf file in downloads folder (Android) - for the ios you can access pdf file through a path (which is saved somewhere in temp directory) and can send or share.
Hope this helps you.
Here is the solution of download on mobile with jspdf
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent))
{
var blob = pdf.output();
window.open(URL.createObjectURL(blob));
}
else
{
pdf.save('filename.pdf');
}
Here is the example if you're using the Cordova platform for your development:
https://github.com/saharcasm/Cordova-jsPDF-Email
The workaround of the pdf not being downloaded in the devices is to use cordova-plugin-file.
Use the output method on the doc that will give you the raw pdf which needs to be written & saved in a file.
For example,
var doc = new JsPDF();
//... some work with the object
var pdfOutput = doc.output("blob"); //returns the raw object of the pdf file
The pdfOutput is then written on an actual file by using the file plugin.
The easiest way which works on both Desktop and Mobile is to use:
window.open(doc.output("bloburl"), "_blank");