I want to download the image file from web. This is my code :
import React from 'react'
import * as Sharing from "expo-sharing";
import * as FileSystem from 'expo-file-system';
import * as MediaLibrary from 'expo-media-library';
import { getCurrentDateTime } from './UtilityHelper';
import { messageBoxOK } from './AlertHelper';
export const downloadFileIOS = async (url, ticker) => {
let filename = ticker + "_" + getCurrentDateTime() + ".png";
MediaLibrary.requestPermissionsAsync();
if (filename !== null) {
FileSystem.downloadAsync(
url,
FileSystem.documentDirectory + filename
).then(async ({uri}) => {
console.log('Finished downloading to ', uri);
MediaLibrary.createAssetAsync(uri).then(asset => {
console.log('asset', asset);
MediaLibrary.createAlbumAsync('My Gallery', asset, false)
.then(() => {
messageBoxOK("Download", "Success");
})
.catch(error => {
messageBoxOK("Download", "Failed");
});
});
}).catch(error => {
console.error(error);
});
}
};
It worked well on Android but not for IOS.
For IOS I got this error :
[Error: Directory for
'file:///Users/dennisliu/Library/Developer/CoreSimulator/Devices/632FD441-0040-4E1A-AA4E-52A5C015C304/data/Containers/Data/Application/EBFB20FF-EAD8-40FE-BE4D-1D1719A633D2/Documents/ASII_2022-09-07
07:37:36.png' doesn't exist. Please make sure directory '(null)'
exists before calling downloadAsync.]
I have allow the permission in IOS.
What could be the problem ?
I found the problem.
The problem is the filename : ASII_2022-09-07 07:37:36.png is not valid and causing the error.
When I change the filename to : AAA.png it worked.
Related
I'm still new to React Native may I know how can I create csv file in react native using expo? I've seen people suggesting expo-file-system as they said it is not recommended to use react-native-fs if using expo but I not sure how to use it. is it using FileSystem.writeAsStringAsync(fileUri, contents, options)?
Here is an example of a function that creates a simple .csv file
I'm using react-native-csv to generate the .csv.
expo-media-library is used to move the file from a folder accessible from the app to a publicly accessible folder.
import { jsonToCSV, readRemoteFile } from 'react-native-csv';
import * as FileSystem from 'expo-file-system';
import * as Permissions from 'expo-permissions';
import * as MediaLibrary from 'expo-media-library';
export async function makeCSV() {
const jsonData = `[
{
"Column 1": "Name",
"Column 2": "Surname",
"Column 3": "Email",
"Column 4": "Info"
}
]`;
const CSV = jsonToCSV(jsonData);
// Name the File
const directoryUri = FileSystem.documentDirectory;
const fileUri = directoryUri + `formData.csv`;
// Ask permission (if not granted)
const perm = await Permissions.askAsync(Permissions.MEDIA_LIBRARY);
if (perm.status != 'granted') {
console.log("Permission not Granted!")
return;
}
// Write the file to system
FileSystem.writeAsStringAsync(fileUri, CSV)
try {
const asset = await MediaLibrary.createAssetAsync(fileUri);
const album = await MediaLibrary.getAlbumAsync('forms');
console.log(album)
if (album == null) {
await MediaLibrary.createAlbumAsync('forms', asset, true);
} else {
await MediaLibrary.addAssetsToAlbumAsync([asset], album, true);
}
} catch (error) {
console.log(error);
}
}
I am really struggling to find where I am going wrong with this. I am trying to move the picked (ImagePicker) image from cache to app scope directory folder named images/. I created a directory images/ using FileSystem.makeDirectoryAsync but while trying to move the picked image to this directory I am getting an error. Please can someone help me I am really struggling
Expected Result
The image successfully moves to the images/ directory
Actual Result
[Unhandled promise rejection: Error: File 'file:///var/mobile/Containers/Data/Application/318CFCE4-16DF-44DD-92B3-39DECA61EA14/Library/Caches/ExponentExperienceData/%2540user%252FtestApp/ImagePicker/ECD218AE-3DD3-429F-B1F5-469DA1AC661C.jpg' could not be moved to
'file:///var/mobile/Containers/Data/Application/318CFCE4-16DF-44DD-92B3-39DECA61EA14/Documents/ExponentExperienceData/%2540user%252FtestApp/images/ECD218AE-3DD3-429F-B1F5-469DA1AC661C.jpg/'.]
This is my code:
import React, { useEffect, useState } from "react";
import {Text,View,TouchableOpacity,Alert,} from "react-native";
import * as ImagePicker from "expo-image-picker";
import * as Permissions from "expo-permissions";
import * as FileSystem from "expo-file-system";
const ImageCard = (props) => {
const { handlePickedImage } = props;
const [image, setImage] = useState("");
// Create any app folders that don't already exist
export const checkAndCreateFolder = async folder_path => {
const folder_info = await FileSystem.getInfoAsync(folder_path);
if (!Boolean(folder_info.exists)) {
// Create folder
console.log("checkAndCreateFolder: Making " + folder_path);
try {
await FileSystem.makeDirectoryAsync(folder_path, {
intermediates: true
});
} catch (error) {
// Report folder creation error, include the folder existence before and now
const new_folder_info = await FileSystem.getInfoAsync(folder_path);
const debug = `checkAndCreateFolder: ${
error.message
} old:${JSON.stringify(folder_info)} new:${JSON.stringify(
new_folder_info
)}`;
console.log(debug);
}
}
};
const veryfiyPermissons = async () => {
const result = await Permissions.askAsync(Permissions.CAMERA_ROLL);
if (result.status !== "granted") {
Alert.alert(
"Insufficient permissions",
"You need to grant permissions to access Camera Roll",
[{ text: "Okay" }]
);
return false;
}
return true;
};
const selectImageHandler = async () => {
const hasPermisson = await veryfiyPermissons();
if (!hasPermisson) {
return;
}
const image = await ImagePicker.launchImageLibraryAsync({
quality: 0.5,
});
if (image.cancelled) {
randomImage;
} else {
let localUri = image.uri;
let localUriNamePart = localUri.split("/");
const fileName = localUriNamePart[localUriNamePart.length - 1];
const images_folder = `${FileSystem.documentDirectory + 'images/'}`
checkAndCreateFolder(images_folder);
const setTheFile = `${images_folder + `${fileName}/`}`
await FileSystem.moveAsync({
from: localUri,
to: newLocation
}).then((i) => {
setImage(setTheFile);
handlePickedImage(setTheFile);
})
}
};
return (
<View>
<TouchableOpacity onPress={selectImageHandler}>
<Text>Add Photo</Text>
</TouchableOpacity>
</View>
);
};
export default ImageCard;
In a react-native project,I have a zipped file which is in android_assets folder. I want to unzip this folder and copy it into the DocumentDirectoryPath. I have imported react-native-zip-archive and used unzipAssets,but it doesn't seem to work. I have used the following code but I get Error: "./ICF-Package" could not be opened.
const assetPath = './ICF-Package.zip'
const targetPath = ${DocumentDirectoryPath}/ICF-Package.zip
copyfile() {
unzipAssets(assetPath, targetPath)
.then(() => {
console.log('unzip completed!')
})
.catch((error) => {
console.log(error)
})
}
You can install it and try to fix the problem.
$ npm install react-native-fetch-blob
$ react-native link react-native-fetch-blob
import RNFetchBlob from 'react-native-fetch-blob';
import { unzip } from 'react-native-zip-archive';
let dirs = RNFetchBlob.fs.dirs
const documentPath = dirs.DocumentDir
const resourceUrl = 'file:///android_asset/target.zip'
downloadResource = () => {
// Set the path to store on the device
const dirs = RNFetchBlob.fs.dirs.DocumentDir
const homePath = dirs + '/target.zip'
RNFetchBlob
.config({
path: homePath
})
.fetch('GET', resourceUrl)
.progress((received, total) => {
const percentage = Math.floor(received / total * 10000) / 100 + '%'
console.log(percentage)
})
.then(resourceFile => {
console.log('target.zip file download success')
this.unzip(resourceFile);
})
.catch((errorMessage, statusCode) => {
console.log(errorMessage);
})
}
unzip = resourceFile => {
const resourcePath = resourceFile.path()
unzip(resourcePath, documentPath)
.then(path => {
console.log(`unzip sucess: ${path}`)
})
}
We want to include zip file on react native project, for example in project/src/assets/zip folder, include it in the build and extract it to local phone storage on initial App run.
What we want to achieve is, suppose we have a web page that we want to show on react native app. We can decrease the loading time by including the asset for the web page on the project, then told the web page to search for the assets file locally.
We have successfully showing web page with local assets downloaded from internet, in zip format. But We haven’t found on how to achieve the same using zip file on local project.
We have try to import the asset file, then unzip it directly like below with no luck
import zipFile from './assets/zip/file.zip'
import * as RNFS from 'react-native-fs'
import { unzip } from 'react-native-zip-archive'
...
componentDidMount() {
const destination = RNFS.DocumentDirectoryPath + '/'
unzip(zipFile, destination)
.then((result) => {
console.log(result)
})
.catch((error) => {
console.log(error)
})
}
We have try to use react-native-local-resource to get the file with several combinations without luck too
import zipFile from './assets/zip/file.zip'
import * as RNFS from 'react-native-fs'
import { unzip } from 'react-native-zip-archive'
import loadLocalResource from 'react-native-local-resource'
...
componentDidMount() {
const destination = RNFS.DocumentDirectoryPath + '/'
loadLocalResource(assetsZip)
.then((fileContent) => unzip(fileContent, destination))
.then((result) => {
console.log(result)
})
.catch((error) => {
console.log(error)
})
}
import zipFile from './assets/zip/file.zip'
import * as RNFS from 'react-native-fs'
import { unzip } from 'react-native-zip-archive'
import loadLocalResource from 'react-native-local-resource'
...
componentDidMount() {
const destination = RNFS.DocumentDirectoryPath + '/'
const fileName = 'file.zip'
loadLocalResource(assetsZip)
.then((fileContent) => RNFS.writeFile(destination + fileName, fileContent))
.then(() => unzip(destination + fileName, destination))
.then((result) => {
console.log(result)
})
.catch((error) => {
console.log(error)
})
}
How can We extract zip file on project folder into Phone Storage?
EDIT:
while the previous answer was working on debug version of the app, it seems to break on production, because on production resource.uri return file:// uri instead of http:// as in debug version.
to fix the problem we need to check whether it was file uri or http url. then use copy function on react-native-fs instead of download function, if it was file uri.
import zipFile from './assets/zip/file.zip'
import * as RNFS from 'react-native-fs'
import { unzip } from 'react-native-zip-archive'
import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'
...
componentDidMount() {
const destination = RNFS.DocumentDirectoryPath + '/'
const filename = 'file.zip'
const resource = resolveAssetSource(zipFile)
const fileUri = resource.uri
this.loadZipFile(fileUri, destination + filename)
.then((result) => unzip(destination + filename, destination))
.then((result) => {
console.log(result)
})
.catch((error) => {
console.log(error)
})
}
}
loadZipFile = (fileUri, destinationFile) = {
if (fileUri.startWith('file://')) {
return RNFS.copyFile(fileUri, destinationFile)
} else {
const downloadOptions = {
fromUrl: fileUri,
toFile: destinationFile
}
return RNFS.downloadFile(downloadOptions).promise
}
}
this should work on both production and debug version, even sending zip file using code push was working fine on our case.
OLD ANSWER:
After looking on react-native-local-resource source code, We come up with some Idea.
The problem with react-native-local-resource in our case is, that the final result was text, but zip file is not a text. So we try to intercept the code in the middle to get zip file as we want.
After some experiment, we follow react-native-local-resource code until we got the uri of the file, then use react-native-fs to download the file into local storage folder, then unzip those file.
Here is sample of working code on our case
import zipFile from './assets/zip/file.zip'
import * as RNFS from 'react-native-fs'
import { unzip } from 'react-native-zip-archive'
import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'
...
componentDidMount() {
const destination = RNFS.DocumentDirectoryPath + '/'
const filename = 'file.zip'
const resource = resolveAssetSource(zipFile)
const fileUri = resource.uri
const downloadOptions = {
fromUrl: fileUri,
toFile: destination + filename
}
RNFS.downloadFile(downloadOptions).promise
.then((result) => unzip(destination + filename, destination))
.then((result) => {
console.log(result)
})
.catch((error) => {
console.log(error)
})
}
Hope it can help anyone who have meet the same problem
I am trying to learn react native and how to download files to the device. I know you can do this with react native file system but that does not support authentication which I need. React native fetch blob does support this.
For education purposes, I want to have the code from the first answer to this question recreated in react native fetch blob instead of react native file system.
I someone could write sutch an examlple form me I would be super tankfull.
Question: Downloading data files with React Native for offline use
Try this, it's work fine on Android:
export const download = (url, name) => {
const { config, fs } = RNFetchBlob;
let PictureDir = fs.dirs.PictureDir;
let options = {
fileCache: true,
addAndroidDownloads: {
useDownloadManager: true,
notification: true,
path: PictureDir + name,
description: t('downloading_file')
}
};
config(options)
.fetch('GET', url)
.then(res => {
if (res.data) {
alert(t('download_success'));
} else {
alert(t('download_failed'));
}
});
};
downloadImage(){
var date = new Date();
var url = "http://debasish.com/image.jpg";
var ext = this.getExtention(url);
ext = "."+ext[0];
const { config, fs } = RNFetchBlob ;
let PictureDir = fs.dirs.PictureDir
let options = {
fileCache: true,
addAndroidDownloads : {
useDownloadManager : true,
notification : true,
path: PictureDir + "/image_"+Math.floor(date.getTime()
+ date.getSeconds() / 2)+ext,
description : 'Image'
}
}
config(options).fetch('GET', url).then((res) => {
Alert.alert("Download Success !");
});
}
getExtention(filename){
return (/[.]/.exec(filename)) ? /[^.]+$/.exec(filename) :
undefined;
}