Confused on blob:url and converting it to base64 in react-dropzone - reactjs

I am using the package react-dropzone (https://github.com/okonet/react-dropzone) to get images from the user. The user uploads their image and everything is fine, but Im only getting something like "blob:http//blahblah" from it and I need the image to be in base64 png.
my dropzone component:
<Dropzone ref="dropzone" multiple={false} onDrop={this.onDrop.bind(this)} >
{this.state.files ?<img className="img-responsive" src={this.state.files[0].preview}></img>
: <div>Upload Photo</div> }
</Dropzone>
and the drop function that gets the blob url :
onDrop (files ) {
if ( files.length === 0 ) {
alert("upload img please")
return;
}
console.log('Received files: ', files);
this.setState({files:files})
var blobURL = files[0].preview
var reader = new FileReader();
reader.readAsDataURL(blobURL)
}
I would get an error :Uncaught TypeError: Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'.
I think it is because im trying to pass in an object-url that points to a blob, but where would I be able to get blob so I can convert to base64?

I would suggest to use a promise to get the result of async convertion by FileReader.readAsDataURL method. Here's the sample how it can be done:
const promise = new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(files[0])
reader.onload = () => {
if (!!reader.result) {
resolve(reader.result)
}
else {
reject(Error("Failed converting to base64"))
}
}
})
promise.then(result => {
// dispatch or do whatever you need with result
}, err => {
console.log(err)
})

Related

How can I check if the data_URL is returning an image of video ? - Firebase & Next.js/React

The image is uploaded to firebase and returned as a data_URL that looks like this:
https://firebasestorage.googleapis.com/v0/b/app_name/o/posts%2postId?alt=media&token=token
I am trying to check if the file type is a video or an image, then return a div depending on the "mediaType". Because firebase storage doesn't include the file extension in the url, it is difficult to determine the file type.
First attempt:
const [mediaType, setMediaType] = useState(null);
useEffect(() => {
if (postImage) {
const storageRef = firebase.storage().ref();
storageRef.child(postImage).getDownloadURL().then(url => {
fetch(url)
.then(res => res.blob())
.then(blob => {
let type = blob.type;
if (type.startsWith("image")) {
setMediaType("image");
} else if (type.startsWith("video")) {
setMediaType("video");
} else {
setMediaType("other");
console.log("Unknown file format: " + type);
}
});
});
}
}, [postImage]);
Second attempt:
const handleFile = async (e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = async (e) => {
const dataURL = e.target.result;
if (dataURL.startsWith('data:image/')) {
setMediaType('image');
setDataUrl(dataURL);
console.log("Image: " + dataURL);
} else if (dataURL.startsWith('data:video/')) {
setMediaType('video');
setDataUrl(dataURL);
console.log("Video: " + dataURL);
} else {
let response = await fetch(dataURL);
let type = response.headers.get("Content-Type");
if (type.startsWith("image")) {
setMediaType("image");
setDataUrl(dataURL);
} else if (type.startsWith("video")) {
setMediaType("video");
setDataUrl(dataURL);
} else {
setMediaType("other");
console.log("Unknown file format: " + type);
}
}
}
reader.readAsDataURL(file);
}
The div inside my return statement:
<div className="w-full px-3">
{mediaType === 'image' ? <img className="shadow-md w-full" src={postImage || 'default-image.jpg'} alt="" /> : null}
{mediaType === 'video' ? <ReactPlayer layout="fill" url={postImage} config={{file:{attributes:{controlsList:'nodownload'}}}} controls onContextMenu={e => e.preventDefault()}/> : null}
{mediaType === 'other' ? <p>File is not an image or video</p> : null}
</div>
What I would do is add metadata while uploading the file to firebase. You can check the documentation to see how:
//This is a metadata, you can customize these
//as you can see the content type is set to be image/jpeg
var newMetadata = {
cacheControl: 'public,max-age=300',
contentType: 'image/jpeg'
.........
.........
};
You will use this metadata while uploading the file using:
storageRef.updateMetadata(newMetadata).......
And when reading the file read out the metadata that you set to the file to for example detect its type:
storageRef.getMetadata().then((metadata) => {
//use this metadata to know the type here.......
})
Hope this gives you an idea of what to do.

Converting xml file data in js object

I need some help. I have an app that takes *.xml file through input type file, and converts it into js object.
To do that i am using FileReader and xml-js library from here https://www.npmjs.com/package/xml-js
Now I have two problems that I can't handle.
xml file contains cyrillic symbols, in console they display as ������� ��� ���
The second problem is that, in some reasons, I can't set converted object in state.
Here is my code:
Handler and input for file:
handleFile = (event) => {
let file = event.target.files[0];
let reader = new FileReader();
reader.readAsText(file);
reader.onloadend = () => {
let json = xmljs.xml2js(reader.result, {compact: true, spaces: 4});
this.setState({
file: json
}, console.log ('file', json))
}
};
render() {
return (
<div>
<input type="file" onChange={this.handleFile}/>
</div>
)
}
So what shouls I do to display cyrillic symbols and how to set the object in state?
I fixed it.
Now my code looks so:
handleFile = (event) => {
let file = event.target.files[0];
let reader = new FileReader();
reader.readAsText(file, 'windows-1251');
reader.onloadend = () => {
let json = xmljs.xml2js(reader.result, {compact: true, spaces: 4});
this.setState({
file: json
}, () => {console.log('state', this.state)})
}
};
render() {
return (
<div>
<input type="file" onChange={this.handleFile}/>
</div>
)
}
I added 'windows-1251' to define the encoding type, it helps with cyrillic symbols.
Also i changed callback function after setState to () => {console.log('state', this.state)} after that I can see a current state, with contetnt fron xml file.

Reading and displaying images from a zip file in react using jszip

I have a zip file containing some images, mp4s and a text file. The text file i am reading like so:
jszip
.loadAsync(arrayBuffer)
.then(({ files }) => {
const txtFiles = Object.entries(files).filter(([fileName]) =>
fileName.endsWith('.txt'),
);
if (!txtFiles.length) {
throw new Error('No txt files found in archive');
}
return txtFiles
.sort(([a], [b]) => a.length - b.length)[0][1]
.async('string');
})
and it works fine. But I need to get all the images/mp4s in the file as well, so I tried:
jszip
.loadAsync(arrayBuffer)
.then(({ files }) => {
const jpgs = [];
const mediaFiles = Object.entries(files).filter(([fileName]) =>
fileName.endsWith('.jpg'),
);
if (!mediaFiles.length) {
throw new Error('No media files found in archive');
}
mediaFiles.forEach(i => {
blob = new Blob([i], {
type: 'image/jpeg',
});
img = new Image();
img.src = URL.createObjectURL(blob);
jpgs.push(img);
});
return jpgs;
})
now when i try to render the jpgs array, i get a bunch of img elements like so:
<img alt="img" src="blob:http://localhost:3000/d64b16c7-aa9c-49a7-96cc-ed4eafc6a054">
but no images / broken image icons. What I am doing wrong?
Apparently I wasn't using the blob correctly, the following code solved it for me, with thanks to Loris Bettazza:
mediaFiles.forEach(([, image]) => {
image.async('blob').then(blob => {
const img = new Image();
img.src = URL.createObjectURL(blob);
document.body.prepend(img);
});
});

Passing base64 to google vision gives 400 error

I am grabbing frames from the webcam, converting each image bitmap into a base64 string then passing that to the Google vision API. When i do this i am catching an error but it only logs as true. Im new to react and am struggling to see what i am missing.
grabFrame() {
let mediaStreamTrack = this.state.mediaStream.getVideoTracks()[0];
let imageCapture = new window.ImageCapture(mediaStreamTrack);
return imageCapture.grabFrame();
}
uploadFrame() {
this.grabFrame()
.then(function(bitmapImage) {
var canvas = document.createElement("canvas")
canvas.width = bitmapImage.width;
canvas.height = bitmapImage.height;
let context = canvas.getContext("2d")
context.drawImage(bitmapImage, 0, 0);
let base64Image = canvas.toDataURL("image/png")
const request = new vision.Request({
image: new vision.Image({
base64: base64Image,
}),
features: [ new vision.Feature('FACE_DETECTION') ]
})
vision.annotate(request)
.then((response) => {
console.log(`Response: ${response}`)
})
.catch((error) => {
console.log(`Error: ${error}`) >>>> "Error: true"
});
}).catch((error) => {
console.log('grabFrame() error: ', error)
});
}
In the console, all I can see is POST https://vision.googleapis.com/v1/images:annotate?key=xxxxxxxxxxxxxxxxxxx 400
Logging base64Image gives data:image/png;base64,iVBORw0KGgoAA...
Am I missing something?

MERN+ Cloudinary: Unsupported source URL

I'm trying to upload file to cloudinary. Here is part of my react component
...
addItem() {
...
let file = this.fileInput.value;
keywords !== "" && this.props.onAddItem(keywords, place, image);
...
}
render() {
return (
....
<Input
type="file"
innerRef={(input) => {this.fileInput = input}}
name="image"
id="image"
placeholder=""/>
)
}
Here is action file:
export function onAddItem(keywords, place, file, id, isChangebale = false) {
return (dispatch) => {
axios.all([
axios.post('https://api.cloudinary.com/v1_1/myservername/image/upload',
{upload_preset: "mypresetname", file: file}),
axios.post('http://localhost:3001/api/items/', { keywords, place, id, isChangebale })
])
.then(axios.spread((cloudinaryRes, localRes) => {
console.log(cloudinaryRes, localRes);
}))
I receive error xhr.js:178 POST https://api.cloudinary.com/v1_1/testovich/image/upload 400 (Bad Request) and in response headers "X-Cld-Error: Unsupported source URL: C:\fakepath\2017-12-07_19-06-445.png"
When I test using postman I have correct response.
So it looks like I do something wrong when pass file from rect component to action file. How to pass correct path/file to cloudinary?
There were two mistakes:
1. in react component there should be
let file = this.fileInput.files[0];//I upload only one file
instead of
let file = this.fileInput.value;
in action file
export function onAddItem(keywords, place, image, id, isChangebale = false) {
const formData = new FormData();
formData.append("file", image);
formData.append("upload_preset", "mypresetname");
return (dispatch) => {
axios.all([
// AJAX upload request using Axios )
axios.post('https://api.cloudinary.com/v1_1/myservername/image/upload',
formData,
instead of:
export function onAddItem(keywords, place, file, id, isChangebale = false) {
return (dispatch) => {
axios.all([
axios.post('https://api.cloudinary.com/v1_1/myservername/image/upload',
{upload_preset: "mypresetname", file: file}),
Convert the image to a base64 like const base64Img = data:image/jpg;base64,${file.data};
The file.data represents the data property from response from image picker.
Then I passed the base64Img to data like
return RNFetchBlob.fetch('POST', apiUrl, headerProps, [ { name: 'file', fileName: file.fileName, type: file.type, data: base64Img } ]);
Hope it helps.

Resources