I wanted to upload images and display the image which was chosen, How to display the image after choosing. this is my code, help me display the image, I made the function to post the image, I can post multiple images in one click but i can't display the image to preview before upload , i try to use file reader but cannot display and upload.
const [pImg, setPImg] = useState([]);
const [images, setImages] = useState([]);
const addImg = (ImagesPostDto) => {
const data2 = new FormData();
[...ImagesPostDto].forEach((Images) => {
data2.append("ImagesPostDto", Images);
});
Axios.post(`/shop/${shopID}/Product/${pID}/Images`, data2)
.then((res) => {
if (res.status === 200) {
setMessage({
data: `${res.data.MESSAGE}`,
type: "alert-success",
});
onShowAlert();
}
})
.catch((err) => {
setMessage({
data: `${err.response.data.MESSAGE}`,
type: "alert-danger",
});
setLoading(false);
onShowAlert();
});
};
const handleImageChange = (e) => {
e.preventDefault();
const ProductImg = e.target.files;
setPImg(ProductImg);
const reader = new FileReader();
reader.onloadend = () => {
setPImg(ProductImg);
setImages(reader.result);
};
reader.readAsDataURL(ProductImg);
};
const handleProductSubmit = (event) => {
event.preventDefault();
addImg(pImg);
};
return (
<div>
<Form>
<p>
<Label htmlFor="file">Upload images</Label>
<input
type="file"
id="file"
onChange={handleImageChange}
accept="image/png, image/jpg, image/jpeg"
multiple
/>
</p>
</Form>
<div className="">
{/* {images.length > 0 ? (
<div>
{images.map((image) => (
<p>
<img src={images} alt="" />
</p>
))}
</div>
) : null} */}
</div>
If you want to render images then, create ObjectURL from files array and set the images State then it should work fine. I have commented the code related to API call so that we can focus on rendering the selected images.You can just simply copy this code and paste it in CodeSandBox it should work fine Here is your code a bit modified:
import "./styles.css";
import { useState } from "react";
export default function App() {
const [pImg, setPImg] = useState([]);
const [images, setImages] = useState([]);
// const addImg = (ImagesPostDto) => {
// const data2 = new FormData();
// [...ImagesPostDto].forEach((Images) => {
// data2.append("ImagesPostDto", Images);
// });
// Axios.post(`/shop/${shopID}/Product/${pID}/Images`, data2)
// .then((res) => {
// if (res.status === 200) {
// setMessage({
// data: `${res.data.MESSAGE}`,
// type: "alert-success"
// });
// onShowAlert();
// }
// })
// .catch((err) => {
// setMessage({
// data: `${err.response.data.MESSAGE}`,
// type: "alert-danger"
// });
// setLoading(false);
// onShowAlert();
// });
// };
const handleImageChange = (e) => {
e.preventDefault();
console.log("event", e);
const ProductImg = [...e.target.files];
const images = ProductImg.map((image) => URL.createObjectURL(image));
console.log("images", images);
setImages(images);
};
// const handleProductSubmit = (event) => {
// event.preventDefault();
// addImg(pImg);
// };
return (
<div>
<form>
<p>
<label htmlFor="file">Upload images</label>
<input
type="file"
id="file"
onChange={handleImageChange}
accept="image/png, image/jpg, image/jpeg"
multiple
/>
</p>
</form>
<div className="">
{images.length > 0 && (
<div>
{images.map((image, index) => (
<p key={index}>
<img src={image} alt="" />
</p>
))}
</div>
)}
</div>
</div>
);
}
You need to fetch the Images from GET API and set the response in setImages than it will show right now the images variable is empty array.
As far I can understand your requirement, you need to preview the Images Either when you have selected or After uploaded.
What you can do is, whenever you are preparing the FormData or at the time of change event, you can store each selected file's ObjectURL in another state and Easily can display these images via the State.
Related
I'm trying to upload images to the backend using base64, my upload is fine, but the backend receives an empty string. When I use console log, I get the data. I don't know what went wrong here.
export default function NewPost() {
const [title, setTitle] = useState("");
const [file, setFile] = useState("");
const [baseImage, setBaseImage] = useState("");
const [newPost, setNewPost] = useState({
userId: '',
title: '',
content: '',
photo: '',
});
const handleSubmit = async (e) => {
e.preventDefault();
try {
const res = await axios.post("/posts/", newPost);
window.location.replace("/post/" + res.data._id);
console.log('Saved Successfully');
} catch (err) {
console.log(err);
}
}
const uploadImage = async (e) => {
const file = e.target.files[0];
const base64 = await convertBase64(file);
setBaseImage(base64);
console.log(base64);
setNewPost({ ...newPost, photo: baseImage });
};
const convertBase64 = (file) => {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = () => {
resolve(fileReader.result);
};
fileReader.onerror = (error) => {
reject(error);
};
});
};
return (
<div className="newPostPageContainer">
<form className="newPostForm" onSubmit={handleSubmit}>
<div className="newPostTitleImageUploadCont">
<div className="newPostTitleContainer">
<label className="newPostFormLabel">Post Title:</label>
<input dir="auto" type="text"
onChange={e => setNewPost({ ...newPost, title: e.target.value })} />
</div>
<div className="newPostUploadImgContainer">
<img src={baseImage} alt="" className="newPostUploadImg" />
<label for="file">
<PublishIcon />
</label>
<input
type="file"
id="file"
onChange={(e) => { uploadImage(e); }}
style={{display: "none"}} />
</div>
</div>
<button className="newPostCreateButton" type="submit">
<p className="newPostCreateButtonText">Create</p>
</button>
</form>
</div>
);
}
Before that I used <FileBase64 /> and it was working perfectly. But since it can't be customized I changed to this, trying to do my own function, some help here would be appreciated.
I am running into an issue where regardless of what component is clicked in a list of Job items, it returns the id of the last element only.
I have a key passed to the mapped list:
let activeListings = jobs.filter((job) => !job.isArchived);
activeListings?.map((job) => {
return (
<div key={job._id} className="bg-white w-8/12 py-4 my-4">
<Job
job={job}
setJob={setJobs}
handleShowDetailsToggle={handleShowDetailsToggle}
archiveToggler={archiveToggler}
setShowingResumeModal={setShowingResumeModal}
setShowingCoverLetterModal={setShowingCoverLetterModal}
showingResumeModal={showingResumeModal}
showingCoverLetterModal={showingCoverLetterModal}
getJobs={getJobs}
key={job._id}
/>
</div>
);
})
In the Job component, there is a button that when clicked opens a modal to select a file to upload and a preview.
Regardless of which Job component the Upload component is called from, it always gets passed the job._id from the final Job component listed.
Here is the resume modal component that opens on the button click:
import React, { useState } from "react";
import Upload from "../../Upload";
const AddNewResumeModal = (props) => {
const { setShowingResumeModal, job, getJobs, id } = props;
const [previewSource, setPreviewSource] = useState("");
return (
<div className="resume-modal-background">
<div className="resume-modal-container">
<button
className="close-modal-btn"
onClick={() => setShowingResumeModal(false)}
>
×
</button>
<h1 className="resume-modal-title">Upload Resume</h1>
<button onClick={() => console.log(job._id)}>Test2</button>
<Upload
resume={true}
setShowingResumeModal={setShowingResumeModal}
job={job}
getJobs={getJobs}
previewSource={previewSource}
setPreviewSource={setPreviewSource}
/>
<div></div>
</div>
</div>
);
};
export default AddNewResumeModal;
and the Upload component it renders:
import React, { useState } from "react";
import { Viewer } from "#react-pdf-viewer/core";
import "#react-pdf-viewer/core/lib/styles/index.css";
const URL = "http://localhost:8000";
const Upload = (props) => {
const {
setShowingResumeModal,
job,
getJobs,
previewSource,
setPreviewSource,
} = props;
const [fileInputState, setFileInputState] = useState("");
// const [previewSource, setPreviewSource] = useState("");
const handleFileInputChange = (e) => {
const file = e.target.files[0];
console.log(file);
previewFile(file);
};
const previewFile = (file) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
setPreviewSource(reader.result);
};
};
const handleSubmitFile = (e) => {
e.preventDefault();
if (!previewSource) return;
uploadFile(previewSource, job._id);
getJobs();
setShowingResumeModal(false);
};
const uploadFile = async (base64encodedFile, id) => {
console.log(base64encodedFile);
console.log(id);
try {
await fetch(`${URL}/upload/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ data: base64encodedFile }),
});
} catch (error) {
console.error(error);
}
};
return (
<div>
<form>
<input
type="file"
name="pdf"
id="pdf"
accept=".pdf"
onChange={handleFileInputChange}
value={fileInputState}
className="form-input"
onClick={() => console.log(job._id)}
/>
</form>
<div className="preview-container">
{previewSource ? (
<div className="viewer-container">
<Viewer fileUrl={previewSource} />
</div>
) : (
<div className="no-preview-source">Preview area</div>
)}
</div>
<button
onClick={handleSubmitFile}
type="submit"
className="upload-submit-btn"
>
Upload Resume
</button>
</div>
);
};
export default Upload;
Please help me understand how to correctly pair each job listing so that the correct id of the job is passed to the backend.
I was following one of the tutorials with React js when this isuue came up. I am using Cloudinary React SDK (React image and video upload). I am using their Upload Widget. But when I press the button to open the widget it gives me this error - 'TypeError: Cannot read property 'createUploadWidget' of undefined'
Here's the script src - <script src="https://widget.cloudinary.com/v2.0/global/all.js" type="text/javascript" ></script>
Here's the code of App.js
import React, { useState } from "react";
import "./App.css";
export default function App() {
const [imageUrl, setimageUrl] = useState(null);
const [imageAlt, setimageAlt] = useState(null);
const handleImageUpload = () => {
const { files } = document.querySelector('input[type="file"]');
const imageFile = document.querySelector('input[type="file"]');
// destructure the files array from the resulting object
const filesa = imageFile.files;
// log the result to the console
console.log("Image file", filesa[0]);
const formData = new FormData();
formData.append("file", files[0]);
// replace this with your upload preset name
formData.append("upload_preset", "xxxxxxx");
const options = {
method: "POST",
body: formData,
};
// replace cloudname with your Cloudinary cloud_name
return fetch(
"https://api.Cloudinary.com/v1_1/xxxxxx/image/upload",
options
)
.then((res) => res.json())
.then((res) => {
setimageUrl(res.secure_url);
setimageAlt(`An image of ${res.original_filename}`);
})
.catch((err) => console.log(err));
};
const openWidget = () => {
// create the widget
const widget = window.Cloudinary.createUploadWidget(
{
cloudName: "xxxxxx",
uploadPreset: "xxxxx",
},
(error, result) => {
if (result.event === "success") {
setimageUrl(result.info.secure_url);
setimageAlt(`An image of ${result.info.original_filename}`);
}
}
);
widget.open(); // open up the widget after creation
};
return (
<div className="app">
<section className="left-side">
<form>
<div className="form-group">
<input type="file" />
</div>
<button type="button" className="btn" onClick={handleImageUpload}>
Submit
</button>
<button type="button" className="btn widget-btn" onClick={openWidget}>
Upload Via Widget
</button>
</form>
</section>
<section className="right-side">
<p>The resulting image will be displayed here</p>
{imageUrl && (
<img src={imageUrl} alt={imageAlt} className="displayed-image" />
)}
</section>
</div>
);
}
Any help is greatly appreciated !
Cloudinary should be referenced in a lowercase -
...
const widget = window.cloudinary.createUploadWidget(
...
I have been trying to implement an S3 bucket on my website to store uploaded images to amazon's cloud. In order to do this, some HTML boiler-plate is required. I made an HTML file and copied boiler-plate and would like to utilize this on my .js file now, which contains the webpage where I would like this information to be displayed.
Here is the HTML file:
<html>
<body>
<h1>Edit your account</h1>
<hr>
<h2>Your avatar</h2>
<input type="file" id="file-input">
<p id="status">Please select a file</p>
<img style="border:1px solid gray;width:300px;" id="preview" src="/images/default.png">
<h2>Your information</h2>
<form method="POST" action="/save-details">
<input type="hidden" id="avatar-url" name="avatar-url" value="/images/default.png">
<input type="text" name="username" placeholder="Username"><br>
<input type="text" name="full-name" placeholder="Full name"><br><br>
<hr>
<h2>Save changes</h2>
<input type="submit" value="Update profile">
</form>
<script>
/*
Function to carry out the actual PUT request to S3 using the signed request from the app.
*/
function uploadFile(file, signedRequest, url){
const xhr = new XMLHttpRequest();
xhr.open('PUT', signedRequest);
xhr.onreadystatechange = () => {
if(xhr.readyState === 4){
if(xhr.status === 200){
document.getElementById('preview').src = url;
document.getElementById('avatar-url').value = url;
}
else{
alert('Could not upload file.');
}
}
};
xhr.send(file);
}
/*
Function to get the temporary signed request from the app.
If request successful, continue to upload the file using this signed
request.
*/
function getSignedRequest(file){
const xhr = new XMLHttpRequest();
xhr.open('GET', `/sign-s3?file-name=${file.name}&file-type=${file.type}`);
xhr.onreadystatechange = () => {
if(xhr.readyState === 4){
if(xhr.status === 200){
const response = JSON.parse(xhr.responseText);
uploadFile(file, response.signedRequest, response.url);
}
else{
alert('Could not get signed URL.');
}
}
};
xhr.send();
}
/*
Function called when file input updated. If there is a file selected, then
start upload procedure by asking for a signed request from the app.
*/
function initUpload(){
const files = document.getElementById('file-input').files;
const file = files[0];
if(file == null){
return alert('No file selected.');
}
getSignedRequest(file);
}
/*
Bind listeners when the page loads.
*/
(() => {
document.getElementById('file-input').onchange = initUpload;
})();
</script>
<div id="root"></div>
</body>
Here is the .js file:
import React, { useState } from "react";
import { Typography, Button, Form, message, Input, Icon } from "antd";
import FileUpload from "../../utils/FileUpload";
import FileUploadNew from "../../utils/fileUploadNew";
import ReactDOM from 'react-dom'
import Axios from "axios";
const { Title } = Typography;
const { TextArea } = Input;
const num = 20
const Continents = [
{ key: 1, value: "MLA" },
{ key: 2, value: "APA" },
];
function UploadProductPage(props) {
const [TitleValue, setTitleValue] = useState("");
const [SubjectValue, setSubjectValue] = useState("");
const [DescriptionValue, setDescriptionValue] = useState("");
const [PriceValue, setPriceValue] = useState(0);
const [ContinentValue, setContinentValue] = useState(1);
const [imageData, setImageData] = useState("");
const [Images, setImages] = useState("");
const onTitleChange = (event) => {
setTitleValue(event.currentTarget.value);
};
const onSubjectChange = (event) => {
setSubjectValue(event.currentTarget.value);
};
const onDescriptionChange = (event) => {
setDescriptionValue(event.currentTarget.value);
};
const onPriceChange = (event) => {
setPriceValue(event.currentTarget.value);
};
const onContinentsSelectChange = (event) => {
setContinentValue(event.currentTarget.value);
};
const updateImages = (newImages) => {
setImages(newImages);
};
const onSubmit = (event) => {
event.preventDefault();
if (
!TitleValue ||
!SubjectValue ||
!DescriptionValue ||
!PriceValue ||
!ContinentValue ||
!Images
) {
return alert("fill all the fields first!");
}
const variables = {
writer: props.user.userData._id,
title: TitleValue,
subject: SubjectValue,
description: DescriptionValue,
price: PriceValue,
images: Images,
continents: ContinentValue,
};
Axios.post("/api/product/uploadProduct", variables).then((response) => {
if (response.data.success) {
alert("Product Successfully Uploaded");
} else {
alert("Failed to upload Product");
}
});
};
const picData = (value) => {
setImages(value);
};
return (
<div style={{ maxWidth: "700px", margin: "2rem auto" }}>
<div style={{ textAlign: "center", marginBottom: "2rem" }}>
<Title level={2}> Upload Prompt</Title>
</div>
<Form onSubmit={onSubmit}>
{/* DropZone */}
{/* <FileUpload refreshFunction={updateImages} /> */}
{/* kamran's code */}
<FileUpload imagePath={picData} refreshFunction={updateImages} />
<br />
<br />
<label>Title</label>
<Input onChange={onTitleChange} value={TitleValue} />
<br />
<br />
<label>Subject</label>
<Input onChange={onSubjectChange} value={SubjectValue} />
<br />
<br />
<label>Description</label>
<TextArea onChange={onDescriptionChange} value={DescriptionValue} />
<br />
<br />
<label>Number of pages</label>
<Input onChange={onPriceChange} value={PriceValue} type="number" />
<br />
<br />
<label>Format</label>
<br>
</br>
<select onChange={onContinentsSelectChange} value={ContinentValue}>
{Continents.map((item) => (
<option key={item.key} value={item.key}>
{item.value}{" "}
</option>
))}
</select>
<br />
<br />
<Button onClick={onSubmit}>Submit</Button>
</Form>
</div>
);
}
export default UploadProductPage;
I have been reading and trying to use getdocumentbyID but it's not working. No errors are being shown. Any help is appreciated
React makes it easy to bind functions without having to select first then bind functions to the DOM.
Put your function above your return statement
const picData = (value) => {
setImages(value);
};
const uploadFile = (file, signedRequest, url) => {
const xhr = new XMLHttpRequest();
xhr.open('PUT', signedRequest);
xhr.onreadystatechange = () => {
if(xhr.readyState === 4){
if(xhr.status === 200){
document.getElementById('preview').src = url;
document.getElementById('avatar-url').value = url;
}
else{
alert('Could not upload file.');
}
}
};
xhr.send(file);
}
const getSignedRequest = (file) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', `/sign-s3?file-name=${file.name}&file-type=${file.type}`);
xhr.onreadystatechange = () => {
if(xhr.readyState === 4){
if(xhr.status === 200){
const response = JSON.parse(xhr.responseText);
uploadFile(file, response.signedRequest, response.url);
}
else{
alert('Could not get signed URL.');
}
}
};
xhr.send();
}
const initUpload = () => {
const files = document.getElementById('file-input').files;
const file = files[0];
if(file == null){
return alert('No file selected.');
}
getSignedRequest(file);
}
return (
...rest of code here
In the DOM portion of your code, add the onChange to the input
<input type="file" id="file-input" onChange={initUpload}>
You can force React to render html by using dangerouslySetInnerHTML as props in a component.
Here is a sample implementation:
hello.html
<h1>Hello world</h1>
In your App.js
import React from "react";
import "./styles.css";
import hello from "./hello.html";
export default function App() {
return (
<div className="App">
<div dangerouslySetInnerHTML={{ __html: hello }} />
</div>
);
In your case, import the html file just like a regular module by using import htmlFile from './htmfile.html' and write it in a div tag with dangerouslySetInerHTML
There's a reason on why it is considered dangerous to force React to render native .html file in your component. You may want to visit this link to know more about div attributes and XSS.
I'm trying to make a file uploader in ReactJS. I managed to get it all done, except for the part that I show the image to the user. I'm able to make the name show up, but the image does not appear.
I think it's going to be easier if I show the code, so, here it goes
FileUploader Component
import React, { useState } from 'react';
import axios from 'axios'
function FileUploader() {
const [file, setFile] = useState('')
const [fileName, setFileName] = useState('Choose File')
const [selectedFile, setSelectedFile] = useState({
filePath: '',
fileName: ''
})
const handleChange = e => {
setFile(e.target.files[0])
setFileName(e.target.files[0].name)
}
const handleUpload = async e => {
e.preventDefault()
const data = new FormData()
data.append('file', file)
const res = await axios.post('http://localhost:8000/upload', data)
const { path, originalname } = res.data
setSelectedFile({filePath: path, fileName: originalname})
}
return (
<>
<div className="custom-file mb-4">
<input type="file" className="custom-file-input" id="customFile" onChange={handleChange} />
<label className="custom-file-label" htmlFor="customFile">{fileName}</label>
</div>
<button onClick={handleUpload} className='btn btn-primary btn-block mt-4'>Upload</button>
{selectedFile.filePath !== '' ? (
<div className='row mt-5'>
<div className='col-md-6 m-auto'>
<h3 className='text-center'>{selectedFile.fileName}</h3>
<img style={{ width: '100%' }} src={selectedFile.filePath} alt='' />
</div>
</div>
) : null}
</>
);
}
export default FileUploader;
I think you should use blob image by using URL.createObjectUrl
Below is updated code
import React, { useState } from 'react';
import axios from 'axios'
function FileUploader() {
const [file, setFile] = useState('')
const [fileName, setFileName] = useState('Choose File')
const [selectedFile, setSelectedFile] = useState({
filePath: '',
fileName: ''
})
const [blobImage, setBlobImage] = useState() // <= add
const handleChange = e => {
setFile(e.target.files[0])
setFileName(e.target.files[0].name)
setBlobImage(URL.createObjectURL(e.target.files[0])) // <= add
}
const handleUpload = async e => {
e.preventDefault()
const data = new FormData()
data.append('file', file)
const res = await axios.post('http://localhost:8000/upload', data)
const { path, originalname } = res.data
setSelectedFile({filePath: path, fileName: originalname})
}
return (
<>
<div className="custom-file mb-4">
<input type="file" className="custom-file-input" id="customFile" onChange={handleChange} />
<label className="custom-file-label" htmlFor="customFile">{fileName}</label>
</div>
<button onClick={handleUpload} className='btn btn-primary btn-block mt-4'>Upload</button>
{selectedFile.filePath !== '' ? (
<div className='row mt-5'>
<div className='col-md-6 m-auto'>
<h3 className='text-center'>{selectedFile.fileName}</h3>
<img style={{ width: '100%' }} src={blobImage} alt='' /> // <= change src to blobImage
</div>
</div>
) : null}
</>
);
}
export default FileUploader;
I wrote an article about file uploading with React and DnD but it's not that far off from what you're trying to accomplish:
Build A React Drag & Drop Progress File Uploader
My guess is that you're wanting to display the file preview of the image that is about to be uploaded. You can do this by loading the image into the local state, although you'll have to be careful if it's a large image as it can crash the browser.
Click the "Run code snippet" to see it in action.
// main.js
const { useState } = React;
const App = () => {
// State / Props
const [preview, setPreview] = useState(null);
// Functions
const onInputFileChange = event => {
// reset each time
setPreview(null);
// Define supported mime types
const supportedFilesTypes = ['image/jpeg', 'image/png'];
if (event.target.files.length > 0) {
// Get the type of the first indexed file
const { type } = event.target.files[0];
if (supportedFilesTypes.indexOf(type) > -1) {
const reader = new FileReader();
reader.onload = e => { setPreview(e.target.result); };
reader.readAsDataURL(event.target.files[0]);
}
}
};
return (<div><h1>Choose an image</h1><input onChange={onInputFileChange} type="file" name="file" />{preview && <img src={preview} />}</div>);
};
ReactDOM.render(<App />, document.getElementById('root'));
<body>
<div id="root"></div>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<script src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<script type="text/babel" src="main.js"></script>
</body>