I'm building an Ionic app using the Ionic Native File plugin. I need the app to be able to read a file that I've saved in the app itself, but I don't understand where I need to store the file. The official Cordova documentation says something about a certain cordova.file.dataDirectory, whereas the Ionic documentation uses this example:
import { File } from '#ionic-native/file';
constructor(private file: File) { }
...
this.file.checkDir(this.file.dataDirectory, 'mydir').then( //stuff).catch( //stuff);
I don't understand where I need to put my file so that the app can read them.
Do not use file:///android_asset/
You can store the file in file.dataDirectory
Storing the file
I am using http to download a zip. I then unzip and delete the zipped file afterwards.
const down = this.http.downloadFile(uri, {}, {}, path + 'data.zip')
.then(data => {
console.log(data.status);
console.log("DOWNLOAD SUCCESS");
this.desc = "Extracting";
this.loading = 100;
this.downloading = false;
this.zip.unzip(path + 'data.zip', path, (this.UnzipProgress))
.then((result) => {
if(result === 0) this.file.removeFile(path,'data.zip');
if(result === -1) console.log('UNZIP FAILED');
});
})
.catch(error => {
console.log("ERROR::");
console.log(error.status);
console.log(error.error); // error message as string
console.log(error.headers);
});
Retrieving the file
The only thing that worked for me was convertFileSrc()
let win: any = window;
let safeURL = win.Ionic.WebView.convertFileSrc(this.file.dataDirectory+'data/yourFile.png');
Hope this helps
Related
I'm trying to download an excel file with the click of a button in my web application. I can see the data come across from my api request, but when I download the file and try to open it I either get a:
"We found a problem with some content in ... Do you want us to try to recover as much as possible" YES => "This file is corrupt and cannot be opened"
or
"... the file format or file extension is not valid. Verify that theh file has not been corrupted..."
If I open the original file saved it works fine so it's not the file. I think the problem is somewhere in the React Code.
I've looked at a lot of other questions on Stack Overflow about this same topic but none of the answers seem to be working for me.
React
React.useEffect(() => {
if (template && downloadBtn.current != null) {
axios
.get<Blob>(
`/template`,
{ params: { filename: template } }
// responseType: 'blob' or "arraybuffer" don't work for me
)
.then((resp) => {
console.log(resp.data);
var blob = new Blob([resp.data], {
type: resp.headers['content-type'] // tried keeping and removing this
}); // removing this assuming resp.data is already a blob didn't work
console.log(blob); // PK ... b���C���h����ؒ )���G+N�
const url = window.URL.createObjectURL(blob);
console.log(url); // blob:http://localhost:3000/29fd5f64-da6a-4b9c-b4a4-76cce1d691c8
if (downloadBtn.current != null) {
downloadBtn.current.download = template;
downloadBtn.current.href = url;
}
});
}
}, [template, downloadBtn.current]);
Flask
#app.route('/template', methods=['GET'])
def template():
filename = getRouteData(['filename']) # helper function I wrote to get request.body data
print(os.path.join(
app.config['templates_folder'], filename), file=sys.stderr)
return send_file(os.path.join(app.config['templates_folder'], filename))
# adding as_attachment=True doesn't work for me either
# file path is correct
I am trying to get an presigned url image upload working correctly. Currently the upload succeeds when selecting an image from the IOS simulator, however when I actually try to view the file it seems the file is corrupted and will not open as an image. I suspect it has something to do with my FormData but not sure.
export async function receiptUpload(file) {
const date = new Date();
const headers = await getAWSHeaders();
const presignUrl = await request.post(
urls.fileUpload.presignUpload,
{file_name: `${date.getTime()}.jpg`},
{headers}
)
.then(res => res.data);
const formData = new FormData();
formData.append('file', {
name: `${date.getTime()}.jpg`,
uri: file.uri,
type: file.type
});
const fileUpload = presignUrl.presignUrl && await request.put(
presignUrl.presignUrl,
formData
)
.then(res => res.status === 200);
}
I have tried from other fixes to change the file uri like so...
Platform.OS === 'android' ? file.uri : file.uri.replace('file://', '');
however this does not seem to work either.
I did this just recently in my current project and the following code is a working example for my use case. I didn't need to convert to a blob either though I am uploading to AWS S3 so if you are uploading elsewhere that may be the issue.
export const uploadMedia = async (fileData, s3Data, setUploadProgress = () => {}) => {
let sendData = { ...fileData };
sendData.data.type = sendData.type;
let formData = new FormData();
formData.append('key', s3Data.s3Key);
formData.append('Content-Type', fileData.type);
formData.append('AWSAccessKeyId', s3Data.awsAccessKey);
formData.append('acl', 'public-read');
formData.append('policy', s3Data.s3Policy);
formData.append('signature', s3Data.s3Signature);
formData.append('file', sendData.data);
return axios({
method: 'POST',
url: `https://${s3Data.s3Bucket}.s3.amazonaws.com/`,
data: formData,
onUploadProgress: progressEvent => {
let percentCompleted = Math.floor((progressEvent.loaded * 100) / progressEvent.total)
setUploadProgress(percentCompleted);
}
})
}
I would first check to see where the issue is occurring. After uploading can you view it on whatever storage service you are trying to upload it to. If so it's something on React Native side. If it doesn't ever get uploaded to the location you know its an error in your upload process. Might help you track the exact location of the error.
I had to do this recently for a project. I believe the data is a base64 string when coming directly from the file input. So the issue is your are uploading a base64 string not the image by simply passing the data field. I had to process it before uploading to the signed URL with the following method.
private dataUriToBlob(dataUri) {
const binary = atob(dataUri.split(',')[1]);
const array = [];
for (let i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], { type: 'image/jpeg' });
}
This answer fixed it for me: How can I upload image directly on Amazon S3 in React Native?
I had tried uploading with axios and fetch with FormData. The download went through but the image file was not readable, even when downloaded to my Mac from the S3 console:
The file "yourfile.jpg" could not be opened. It may be damaged or use a file format that Preview doesn’t recognize.
Only after trying to upload with XHR with the correct Content-Type header did it work. Your signedUrl should be correct as well, which seems to be the case if the download goes through.
im trying to add multiple images using react app and send them to backend code to store them in mongodb
here is the code for the backend :
link
and this is the frontend link
so this code works for just one image
i need to be able to add multiple images
Server
Since you are using multer, change the upload.single() function to upload.array().
For example:
app.post("/addItem",
upload.array('product-image', 4), // 'product-image' is field name and 4 is the max number of files allowed
(req, res) => {
console.log(req.files);
// ... rest of the logic
}
)
Check out docs for upload.array()
Client
Change current <input> to allow multiple files:
<input type="file" name="product-image" onChange={this.fileChangeHandler} multiple>
Now save all the images user picked not only the event.target.files[0]:
fileChangeHandler(event) {
let files = event.target.files
this.setState({ selectedFiles: files })
}
Now add them in FormData and upload as usual:
let formData = new FormData()
formData.append("product-image", this.state.selectedFiles)
That's it! Hope it helps.
PS: I don't think files should be added to state. You can simply add them to a class variable. In this answer I explained why and how to do that.
Update:
You need to loop over the files now. Your /addItem endpoint's code will look something like this:
app.post("/addItem", upload.array('product-image', 4), (req, res) => {
console.log(req.files);
let paths = [];
req.files.forEach(file => {
console.log("new file location", file.path)
let extension = file.originalname.split(".").pop()
fs.rename(file.path, file.path + "." + extension, () => {})
paths.push("/" + file.filename + "." + extension);
});
console.log("body", req.body)
let itemToStore = {
paths: paths, // notice this `paths` now, it was `path`
description: req.body.description
}
console.log("we are adding", itemToStore)
itemData.push(itemToStore)
console.log("updated itemData:", itemData)
res.send(JSON.stringify(itemData))
})
I didn't modify your code, just added a loop. Your 'path' of undefined error should go away.
I'm trying to upload audio file to Firebase Storage in my Ionic2 project.
First I recorded a audio file using Media plugin (Cordova plugin), and this file is playing well. From the Android storage and from the media plugin method (this.media.play()...;).
Second I need to push the recorded file to Firebase Storage.
this is my code:
let storageRef = firebase.storage().ref();
let metadata = {
contentType: 'audio/mp3',
};
let filePath = `${this.file.externalDataDirectory}`+`${this.fileName}`;
const voiceRef = storageRef.child(`voices/${this.fileName}`);
var blob = new Blob([filePath], {type: 'audio/mp3'});
voiceRef.put(blob);
After reading the Firebase doc, I can push blob to Firebase.
The file is successfully pushed to Firebase Storage with empty data (95 Byte).
this is screenshot:
The problem isn't a Firebase issue
My problem is solved by using the File cordova plugin method (readAsDataURL()) and the putString(fileBase64,firebase.storage.StringFormat.DATA_URL) method.
First, I create a file reference:
let filePath = "this.file.externalDataDirectory" + "this.fileName";
Then I transform the file to a base64 string by using the readAsDataURL method that returns a promise containing the file as a string base64. Also, I push the file to Firebase using the putString method that has two parameters the File that returned by the readAsDataURL and the second is firebase.storage.StringFormat.DATA_URL.
My Final code:
let storageRef = firebase.storage().ref();
let metadata = {
contentType: 'audio/mp3',
};
let filePath = `${this.file.externalDataDirectory}` + `${this.fileName}`;
this.file.readAsDataURL(this.file.externalDataDirectory, this.fileName).then((file) => {
let voiceRef = storageRef.child(`voices/${this.fileName}`).putString(file, firebase.storage.StringFormat.DATA_URL);
voiceRef.on(firebase.storage.TaskEvent.STATE_CHANGED, (snapshot) => {
console.log("uploading");
}, (e) => {
reject(e);
console.log(JSON.stringify(e, null, 2));
}, () => {
var downloadURL = voiceRef.snapshot.downloadURL;
resolve(downloadURL);
});
});
That's working fine for me.
Thanks.
Is it possible to save an image to the android's local file system so it can be viewed from the phone's 'Gallery' and in a folder??
I found this react-native-fs library but after studying the documentation and working through an example I am still unsure if it is possible.
Thanks
For anyone having the same problem, here is the solution.
Solution
I am using the File System API from the react-native-fetch-blob library. This is because I tought it was way better documented and easier to understand than the 'react-native-fs' library.
I request an image from the server, receive a base64 and I then save it to the Pictures directory in the android fs.
I save the image like this:
var RNFetchBlob = require('react-native-fetch-blob').default;
const PictureDir = RNFetchBlob.fs.dirs.PictureDir;
getImageAttachment: function(uri_attachment, filename_attachment, mimetype_attachment) {
return new Promise((RESOLVE, REJECT) => {
// Fetch attachment
RNFetchBlob.fetch('GET', config.apiRoot+'/app/'+uri_attachment)
.then((response) => {
let base64Str = response.data;
let imageLocation = PictureDir+'/'+filename_attachment;
//Save image
fs.writeFile(imageLocation, base64Str, 'base64');
console.log("FILE CREATED!!")
RNFetchBlob.fs.scanFile([ { path : imageLocation, mime : mimetype_attachment } ])
.then(() => {
console.log("scan file success")
})
.catch((err) => {
console.log("scan file error")
})
}).catch((error) => {
// error handling
console.log("Error:", error)
});
},
The following code that is in the above method refreshes the Gallery otherwise the images would not display untill the phone is turned off and back on again.
RNFetchBlob.fs.scanFile([ { path : imageLocation, mime : mimetype_attachment } ])
.then(() => {
console.log("scan file success")
})
.catch((err) => {
console.log("scan file error")
})
Enjoy!
You can absolutely do this with react-native-fs. There's a PicturesDirectoryPath constant which isn't mentioned in the README for the project; if you save a file into there it should appear in the Gallery app. If you want it to appear in your own album, just make a new directory in that folder and save the file into there, eg
const myAlbumPath = RNFS.PicturesDirectoryPath + '/My Album'
RNFS.mkdir(myAlbumPath)
.then(/* write/copy/download your image file into myAlbumPath here */)
I don't have full example code anymore sorry, because I ended storing images in my app's private cache directory instead. Hope this helps anyway!