I am picking photos from gallery and upload to server but since few days I noticed that some photos have extension heic and browsers can't render those images.
1. Is there a way to extract photos from uploaded heic?
2. How in react native I can get jpeg from this format?
You can convert it on the server side, with the help of this awesome lib Tifig.
https://github.com/monostream/tifig
You can also use API's like https://cloudconvert.com/formats/image/heic to convert either on server side or on the mobile (client side).
I'm assuming you are using react-native-image-picker, which is the community library and most used one.
Actually is not necessary to install another module, just grab always the uri from the response and update the filename in case you are having a heic image. Code example:
const options = {
title: 'Select Avatar',
storageOptions: {
skipBackup: true,
path: 'images'
},
noData: true,
quality: 1,
mediaType: 'photo'
};
ImagePicker.showImagePicker(options, imgResponse => {
this.setState({ imageLoading: true, avatarMediaId: null });
if ((imgResponse.didCancel) || (imgResponse.error)) {
this.setState({ imageLoading: false });
} else {
let source = {};
let fileName = imgResponse.fileName;
if (Platform.OS === 'ios' && (fileName.endsWith('.heic') || fileName.endsWith('.HEIC'))) {
fileName = `${fileName.split(".")[0]}.JPG`;
}
source = { uri: imgResponse.uri, fileName };
this.uploadImage(source);
}
});
Here's my solution: I am using react-native-image-crop-picker.
In my case, the server doesn't accept if a filename is .HEIC, telling me to use .png or .jpg
So I just:
image.filename.replace(/HEIC/g, 'jpg')
To give you a whole picture:
const formData = new FormData();
formData.append('file', {
uri: image.path,
name: image.filename.replace(/HEIC/g, 'jpg'),
type: image.mime,
});
return await axios.post(`/avatar/${id}`, formData);
Related
I'm having a problem using #react-native-community/cameraroll.
Here's my code:
const handleSaveImageToDevice = async () => {
try {
const uri = await captureRef(viewRef, {
format: 'jpg',
quality: 0.8,
});
const image = CameraRoll.save(uri, {
type: 'photo',
album: 'Generated Image',
});
} catch (e) {
return e;
}
};
As seen in the code I used captureRef to save react elements with ref as image.
The behavior is working as expected. If the user clicks the save button the image will be saved to the designated folder. But the problem is it saves random file name. I want to rename it like for example "generated_image".
When you are capturing there is option for image file name:
const uri = await captureRef(viewRef, {
format: 'jpg',
quality: 0.8,
fileName: 'generated_image'
});
Refer below link:
https://github.com/gre/react-native-view-shot/issues/116#issuecomment-1155523609
I forgot to update this but I already found a way to fix my problem using third party library react-native-fs
viewShot?.current?.capture().then((data) => {
RNFS.writeFile(
RNFS.CachesDirectoryPath +
`/generated-image.jpg`,
data,
'base64',
).then((success) => {
return CameraRoll.save(
RNFS.CachesDirectoryPath +
`/generated-image.jpg`,
{
type: 'photo',
album: 'testing',
},
);
});
});
I am using react-native-image-to-pdf library to convert images to pdf in my react native app. from https://www.npmjs.com/package/react-native-image-to-pdf
var photoPath = ['https://images.pexels.com/photos/20787/pexels-photo.jpg?auto=compress&cs=tinysrgb&h=350','https://images.pexels.com/photos/20787/pexels-photo.jpg?auto=compress&cs=tinysrgb&h=350'];
const myAsyncPDFFunction = async () => {
try {
const options = {
imagePaths: photoPath,
name: 'PDFName',
};
const pdf = await RNImageToPdf.createPDFbyImages(options);
console.log(pdf.filePath);
} catch(e) {
console.log(e);
}
}
but this is giving error Error: Attempt to invoke virtual method 'int android.graphics.Bitmap.getWidth()' on a null object reference
I have also tried giving path as ['./assets/a.png', './assets/b.png']
but still getting same error
Based on the usage example, your photoPath needs to be a local file path and not a remote path.
My recommendation is to first use rn-fetch-blob to download the remote image to the device, and then pass your new local image path to react-native-image-to-pdf. Something like:
RNFetchBlob
.config({
// add this option that makes response data to be stored as a file,
// this is much more performant.
fileCache : true,
})
.fetch('GET', 'http://www.example.com/file/example.png', {
//some headers ..
})
.then(async (res) => {
// the temp file path
console.log('The file saved to ', res.path())
const options = {
imagePaths: [res.path()],
name: 'PDFName',
};
const pdf = await RNImageToPdf.createPDFbyImages(options);
})
from file path remove the text 'file://; with empty string('').
const options = {
imagePaths: [uri.replace('file://', '')],
name: 'FileName',
quality: .9, // optional compression paramter
};
replace('file://', '') it's work for me
So I am trying to use the expo document picker (https://docs.expo.io/versions/latest/sdk/document-picker/) to let the user select an image from their phones file system and display it on the screen. It was working quite recently until I noticed that the promise it sends back stopped returning the correct uri. Before it was returning a uri starting with file:///data/ where as now it returns a uri starting with /data. This new type of uri does not display on the screen and I am wondering how I get back to the old one? I am using await DocumentPicker.getDocumentAsync to get the file in the first place.
I had the exact same problem after upgrading from expo 38 to 42. I did a workaround using expo-file-system.
First I set the copyToCacheDirectory option to false
const result = await DocumentPicker.getDocumentAsync({
type:'*/*',
copyToCacheDirectory: false,
});
That would save the file to the conten:// file space. Once we have the file we can copy it to our file:// location:
const uri = FileSystem.documentDirectory+result.name;
await FileSystem.copyAsync({
from: result.uri,
to: uri
})
Now you should be able to use uri (which includes the file:// path) without problems.
I have same problem from expo SDK 38 to SDK 42,
I find a workaround from here
const encode = uri => {
if (Platform.OS === 'android') return encodeURI(`file://${uri}`)
else return uri
}
...
<Button
onPress = { async () => {
let result = await DocumentPicker.getDocumentAsync({ type: '*/*' })
if (result.type === "success") {
console.log("getDocumentAsync", result);
Alert.alert(
"upload file",
`Whether to upload ${result.name}`,
[
{
text: "Cancel",
onPress: () => {},
style: "cancel"
},
{
text: "OK",
onPress: () => dispatch(action.sendFile(encode(result.uri)))
}
],
{ cancelable: true }
)
}
}}
/>
it work for me!
I you want the file to be accessible for using in WebView, You can do this
const result = await DocumentPicker.getDocumentAsync({
type: "application/pdf",
copyToCacheDirectory: false,
});
This is will give you a uri like this
{
...,
"uri": "content://com.android.externalstorage.documents/document/home%3Ayour-file.pdf",
}
and you can access the file by passing it as uri
{{
uri: uri,
}}
I need to upload a selection of images that user picked from CameraRoll to the LoopBack Component Storage. The thing is that the component storage is working fine, because I can upload and download the files through Postman. But, when I try to upload from react native to loopback, it always returns "No file content upload" with http status 400.
I read a lot of people talking about it and tried everything and none worked for me.
First, I am taking the images from the CameraRoll and my images array looks like this:
[
{
exists: 1,
file: "assets-library://asset/asset.JPG?id=3FF3C864-3A1A-4E55-9455-B56896DDBF1F&ext=JPG",
isDirectory: 0,
md5: "428c2e462a606131428ed4b45c695030",
modificationTime: 1535592967.3309255,
size: 153652,
uri: null
}
]
In the example above I just selected one image.
I transformed to Blob, then I got:
[
{
_data: {
blobId: "3FF3C864-3A1A-4E55-9455-B56896DDBF1F",
name: "asset.JPG",
offset: 0,
size: 153652,
type: "image/jpeg"
}
}
]
So I tried a lot of things after this, tried to send the blob itself as the request body, tried to append to a form data and send the form data, but it doesn't matter the way I try, I always get the "No file content upload" response.
I also tried the example from Facebook, didn't work: https://github.com/facebook/react-native/blob/master/Libraries/Network/FormData.js#L28
The way I am trying now:
In my view:
finalizarCadastro = async () => {
let formData = new FormData();
let blobs = [];
for(let i=0;i<this.state.fotos.length;i++){
let response = await fetch(this.state.fotos[i]);
let blob = await response.blob();
blobs.push(blob);
}
formData.append("files", blobs);
this.props.servico.criar(formData);
}
And the function that send to my server:
criar: (servico) => {
this.setState({carregando: true});
axios.post(`${REQUEST_URL}/arquivos/seila/upload`, servico, {headers: {'content-type': 'multipart/form-data'}}).then(() => {
this.setState({carregando: false});
this.props.alertWithType("success", "Sucesso", "Arquivo salvo com sucesso");
}).catch(error => {
this.setState({carregando: false});
console.log(error.response);
this.props.alertWithType("error", "Erro", error.response.data.error.message);
})
}
I found the solution. So the problem was actually not the code itself, the problem was sending multiple files at the same time. To fix everything, I did this:
this.state.fotos.forEach((foto, i) => {
formData.append(`foto${i}`, {
uri: foto,
type: "image/jpg",
name: "foto.jpg"
});
})
this.props.servico.criar(formData);
And my function that sends the request to the server:
criar: (servico) => {
this.setState({carregando: true});
axios.post(`${REQUEST_URL}/arquivos/seila/upload`, servico).then((response) => {
this.setState({carregando: false});
this.props.alertWithType("success", "Sucesso", "Arquivo salvo com sucesso");
}).catch(error => {
this.setState({carregando: false});
this.props.alertWithType("error", "Erro", error.response.data.error.message);
})
},
So you don't need to set the Content-Type header to multipart/form-data and don't need to transform the images to blob, actually you just need the uri of each one, and I think the type and name attributes are opcional.
I'm using react-native-camera and I'm having trouble getting the image as binary data in react-native. I need this to be able to upload images to our backend. The only thing I manage to get is uri's to the image and then maybe sending that as FormData to the server but that is not recommended as that would require some infrastructure change to our backend.
Is there anyone that know a solution or some tips regarding this issue?
Any thoughts or help is very much appreciated.
If you want to get image as binary data from react-native-camera. I recommend to use react-native-fs to read uri
Example
const RNFS = require("react-native-fs");
// response.uri from react-native-camera
RNFS.readFile(response.uri, "base64").then(data => {
// binary data
console.log(data);
});
If you want to upload image via FormData I recommend rn-fetch-blob
Example
import RNFetchBlob from 'rn-fetch-blob'
// response.uri from react-native-camera
const path = response.uri.replace("file://", "");
const formData = [];
formData.push({
name: "photo",
filename: `photo.jpg`,
data: RNFetchBlob.wrap(path)
});
let response = await RNFetchBlob.fetch(
"POST",
"https://localhost/upload",
{
Accept: "application/json",
"Content-Type": "multipart/form-data"
},
formData
);
An alternative if you're already using react-native-camera is upon capturing the image, you request it as base64 directly as such:
takePicture = async function(camera) {
const options = { quality: 0.5, base64: true, doNotSave: true }
const data = await camera.takePictureAsync(options)
console.log(data.base64)
}
If your goal is to only snap the picture, show a preview perhaps then upload to the server and move on, then the benefit of this approach is that it won't save that photo in your device cache (doNotSave: true). Which means that you don't need to worry about cleaning those up after you're done.
You can use 'react-native-image-crop-picker' to pick image and video. Set following property to true
includeBase64: true
and image file content will be available as a base64-encoded string in the data property
ImagePicker.openPicker({
mediaType: "photo",
includeBase64: true // this will give 'data' in response
})
.then((response) => {
console.log(resonse.data)
})
.catch((error) => {
alert(error)
});