I'm trying to develop a program that takes an image from the user's webcam (using react-webcam library), and then compresses it to send to a backend API, however I cannot seem to compress the base 64 jpeg image obtained from the webcam.
I have tried using browser-image-compression to try and compress it, however it expects the image data to be a blob instead of a jpeg format. And I cannot find a way to convert the base 64 string to a blob. Please find below the code:
import Webcam from "react-webcam"
import React, { useState, useRef, useEffect } from "react"
import imageCompression from "browser-image-compression"
const App = () => {
const webRef = useRef(null)
const capture = async () => {
const img = webRef.current.getScreenshot()
// I need to convert img to a blob!
const options = { maxSizeMB: 0.5, maxWidthOrHeight: 720, useWebWorker: true };
const compressedImage = await imageCompression(img, options); // It will raise an error here as it expects img to be of type: Blob
// Send compressed image to backend API
}
return (
<>
<div className="webcam-container">
<Webcam
ref={webRef}
mirrored={true}
screenshotQuality={0.5}
className="webcam"
audio={false}
screenshotFormat="image/jpeg"
width={720}
height={720}
/>
<div className="webcam-button-container">
<button onClick={capture}>Capture</button>
</div>
</div>
</>
)
}
export default App
It doesn't really matter whether the image is jpeg or webp, just as long as it is compressed since sending it uncompressed takes way too long.
Thank you in advance for any help!
Related
I am new to react. A task assigned to create drag and drop component. I followed some blogs to do the task, which only accept image file types. Now the task is when clicked on the upload icon it should open file explorer which should only show image type files. I cannot try to figure out how that would work. Part of my codes which i took from various blogs are:
Drag and drop component:
import React from "react";
import { useDropzone } from "react-dropzone";
import UploadIcon from '#mui/icons-material/Upload';
const Dropzone = ({ onDrop, accept }) => {
// Initializing useDropzone hooks with options
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
accept
});
/*
useDropzone hooks exposes two functions called getRootProps and getInputProps
and also exposes isDragActive boolean
*/
return (
<div className="dropzone-div" {...getRootProps()}>
<input className="dropzone-input" {...getInputProps()} accept=".gif,.jpg,.jpeg,.png"/>
<div className="text-center">
<UploadIcon fontSize="large"/>
{isDragActive ? (
<p className="dropzone-content"> Release to drop the files here</p>
) : (
<p className="dropzone-content">
<b> Choose a file </b> or drag it here
</p>
)}
</div>
</div>
);
};
export default Dropzone;
In the app.js
import React, { useCallback,useState } from "react";
import './App.css';
import Form from './components/Form';
import DragDrop from './components/DragDrop';
import ImageList from "./components/ImageList";
import cuid from "cuid";
function App() {
const [images, setImages] = useState([]);
const [errorMessage, setErrorMessage] = useState([]);
const onDrop = useCallback(acceptedFiles => {
// Loop through accepted files
acceptedFiles.map(file => {
// Initialize FileReader browser API
if (!file.name.match(/\.(jpg|jpeg|PNG|gif|JPEG|png|JPG|gif)$/)) {
setErrorMessage('please select valid file image');
//this.setState({ invalidImage: 'Please select valid image.' });
return false;
}
if(file.name.match(/\.(jpg|jpeg|PNG|gif|JPEG|png|JPG|gif)$/)){
const reader = new FileReader();
// onload callback gets called after the reader reads the file data
reader.onload = function(e) {
// add the image into the state. Since FileReader reading process is asynchronous, its better to get the latest snapshot state (i.e., prevState) and update it.
setImages(prevState => [
...prevState,
{ id: cuid(), src: e.target.result }
]);
setErrorMessage();
};
// Read the file as Data URL (since we accept only images)
reader.readAsDataURL(file);
}
return file;
});
}, []);
return (
<main className="App">
<h2 className="App">Drag and Drop Example</h2>
<br />
<div className=".dropzone-div">
<DragDrop onDrop={onDrop} accept={ 'image/*'}/>
</div>
<div className="App">
{errorMessage && <span> {errorMessage} </span>}
<ImageList images={images} />
</div>
</main>
);
}
export default App;
Use an input element with the type as file, like so:
<span>
<label for="upload">Upload</label>
<input id="upload" type="file" accept="image/*" />
</span>
You could of course change the label to your liking, such as an upload icon.
This is a native HTML element that comes with the functionality you want, out of the box. It is tempting to code everything by hand, especially if you're a beginner. Just remember to search for native solutions before you try a new functionality, or even better, familiarize yourself with the docs of the language/framework you're using.
By the way, here's the MDN doc for the file input element: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file
I am currently building an app where I want there to be a play button that when pressed plays an audio file. My method of doing so is to create a soundplayer component that creates the button and audio sync so that I can just call the component in the main screen I use it in. I can get the soundplayer component to work just fine when I manually put the mp3 file path in it's proper place, but not when I try to pass it in as a parameter. Take a look at my code below and see if you may be able to help resolve the situation.
SoundPlayer Component
import * as React from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
import { Audio } from 'expo-av';
function SoundPlayer({ mp3 }) {
const [sound, setSound] = React.useState();
async function playSound() {
console.log('Loading Sound');
const { sound } = Audio.Sound.createAsync({ mp3 });
setSound(sound);
console.log('Playing Sound');
await sound.playAsync(); }
React.useEffect(() => {
return sound
? () => {
console.log('Unloading Sound');
sound.unloadAsync(); }
: undefined;
}, [sound]);
return (
<View style = {styles.container}>
<Button title="Play Sound" onPress={playSound} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}
})
export default SoundPlayer
App Screen
export default function App() {
return (
<SoundPlayer mp3 = {require('/Users/viswajithkumar/DharmaWorldwide/Screens/assets/Audio/FrenchTouch.mp3')}/>
);
}
According to the Documentation of expo-av, we can see that there are two different methods for static files and remote files to load.
For Static Files (require...) this is the way to load a file
const { sound } = await Audio.Sound.createAsync(require('./assets/Hello.mp3'));
For Remote Files (https://example.com/test.mp3) this is the way to load a file
const { sound } = await Audio.Sound.createAsync({
uri: 'https://example.com/test.mp3',
});
So replacing,
const { sound } = Audio.Sound.createAsync({ mp3 });
with
const { sound } = Audio.Sound.createAsync(mp3);
Should fix the issue. Just make sure the location of the sound file is correct.
Another Approach
A better way to load and play audio files, which I personally feel the best is to use useRef variable for the sound instance. I've created a snack for the implementation here
I want to create a slate.js-based editor component that keeps it's state in markdown. Slate.js docs keep repeating how simple serializing and deserializing state into md should be, but they don't provide an actual way to do it.
I tried achieving such editor with remark-slate-transformer in a very straight-forward way , based on these two examples: remark-slate-transformer, slate:
import React, { useMemo, useState } from "react";
import { createEditor } from "slate";
import { Slate, Editable, withReact } from "slate-react";
import stringify from "remark-stringify";
import unified from "unified";
import markdownParser from "remark-parse";
import { remarkToSlate, slateToRemark } from "remark-slate-transformer";
import { withHistory } from "slate-history";
function markdown2slate(markdown) {
const processor = unified().use(markdownParser).use(remarkToSlate);
return processor.processSync(markdown).result;
}
function slate2markdown(slate) {
const processor = unified().use(slateToRemark).use(stringify);
const ast = processor.runSync({ type: "root", children: slate });
return processor.stringify(ast);
}
export const App = () => {
const editor = useMemo(() => withHistory(withReact(createEditor())), []);
const [value, setValue] = useState("**initialText**");
const onChange = (newVal) => {
setValue(slate2markdown(newVal));
};
const editorValue = markdown2slate(value);
return (
<div className="wrapper">
<Slate editor={editor} value={editorValue} onChange={onChange}>
<Editable />
</Slate>
</div>
);
};
export default App;
sandbox here
But this doesn't work very well. I expect the initial text to appear in bold, but it doesn't. The cursor keeps jumping back to position 0 on every keystroke. Also, when I delete the string (value becomes ''), the editor breaks.
What is the correct, hassle-free way of making an editor component with state stored as markdown?
I'm not sure why you would absolutely want to store the editor state as Markdown, but this just cannot and will not work: you can't simply swap Slate internal state to something different than what it expects, its own object model, and expect it to work.
What you can do is to deserialize Markdown content into a Slate state object, feed that to the editor, let Slate do its thing while you edit and then serialize back to Markdown to do whatever you need to do with it, store it, send it, etc.
I want to be able to select multiple files and send them to my backend but can't figure out how.
In app.js I have a usestate that I send as a prop to my file upload-component.
But after selectig the files I get FileList() with correct number of entries but they are all undefined in my file-state.
If I change the onChange function to :
(e) => setFiles(e.target.files[0])
or any other index I get a correct file. But how do I save the whole file-array directly?
And in the next step when I need to post my files to the backend can I access them by filename or do I need to store them as blobs or byte-arrays to be able to send them in a json-format?
import React from "react";
export const FileUpload = ({ files, setFiles }) => {
return (
<div className="file-upload">
<span className="button">
<i className="material-icons">attachment</i>Choose files
</span>
<input
type="file"
multiple
className="multiple-files"
aria-label="Multiple file upload"
accept="image/*"
onChange={(e) => setFiles(e.target.files)}
/>
</div>
);
};
After some trial and error I came up with this solution:
const fileHandler = e => {
const fileArray = Array.from(e.target.files)
fileArray.map(f => f["id"] = Math.random() * Math.pow(10,16))
setFiles(fileArray)
}
I am VERY new to reactJS and I am trying to create a simple web app that allows me to upload a file and then save that file to my projects directory.
I have tried browserify-fs but its doesn't seem to create the file when I use fs.writeFile
The below code allows me to upload a file but I am struggling to save the file in my project directory
import ReactDOM from 'react-dom';
import Dropzone from 'react-dropzone';
class App extends Component {
onDrop = (acceptedFiles) => {
// Save acceptedFiles in this scripts directory
}
render() {
return (
<Dropzone onDrop={this.onDrop}>
{({getRootProps, getInputProps}) => (
<div {...getRootProps()}>
<input {...getInputProps()} />
Click me to upload a file!
</div>
)}
</Dropzone>
);
}
}
export default App;
ReactDOM.render(
<App />,
document.getElementById('root')
);
browserify-fs stores data in the browser (I'm assuming using local storage, but I can't find a clear statement to that effect).
If you want to store data on the server then you'll need to:
Send the data to the server (using Ajax)
Store that data using server-side code
Use the following function...
function download(content, fileName, contentType) {
var a = document.createElement("a");
var file = new Blob([content], {type: contentType});
a.href = URL.createObjectURL(file);
a.download = fileName;
a.click();
}
...and call as follows...
download('Text to Save to the File', 'TestSave.txt', 'text/plain')