Get base 64 info from upload - reactjs

I have an upload component from ant design library.
import React, {useState} from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import {
Form,
Select,
InputNumber,
Switch,
Radio,
Slider,
Button,
Upload,
Rate,
Checkbox,
Row,
Col,
} from 'antd';
import { UploadOutlined, InboxOutlined } from '#ant-design/icons';
const { Option } = Select;
const formItemLayout = {
labelCol: {
span: 6,
},
wrapperCol: {
span: 14,
},
};
const normFile = e => {
console.log('Upload event:', e);
if (Array.isArray(e)) {
return e;
}
return e && e.fileList;
};
const Demo = () => {
const onFinish = values => {
console.log('Received values of form: ', values);
};
const [state, setState] = useState({
loading: false,
});
function getBase64(img, callback) {
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result));
reader.readAsDataURL(img);
}
const handleChange = info => {
if (info.file.status === 'uploading') {
setState({loading: true});
return;
}
if (info.file.status === 'done') {
// Get this url from response in real world.
getBase64(info.file.originFileObj, imageUrl =>
setState({
imageUrl,
loading: false,
}),
);
}
};
function beforeUpload(file) {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
console.error('You can only upload JPG/PNG file!');
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
console.error('Image must smaller than 2MB!');
}
return isJpgOrPng && isLt2M;
}
const uploadButton = (
<div>
<div className="ant-upload-text">Upload img</div>
</div>
);
const {imageUrl} = state;
return (
<Form
name="validate_other"
{...formItemLayout}
onFinish={onFinish}
initialValues={{
'input-number': 3,
'checkbox-group': ['A', 'B'],
rate: 3.5,
}}
>
<Form.Item
name="upload"
label="Upload"
valuePropName="fileList"
getValueFromEvent={normFile}
>
<Upload
listType="picture-card"
className="avatar-uploader"
showUploadList={false}
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
beforeUpload={beforeUpload}
onChange={handleChange}
>
{imageUrl ? <img src={imageUrl} alt="avatar" style={{width: '100%'}}/> : uploadButton}
</Upload>
</Form.Item>
<Form.Item
wrapperCol={{
span: 12,
offset: 6,
}}
>
<Button type="primary" htmlType="submit">
get data from upload
</Button>
</Form.Item>
</Form>
);
};
ReactDOM.render(<Demo />, document.getElementById('container'));
Now i have an issue when i upload an image and after that click on upload img button. I don't get the base 64 data of image in console.log('Received values of form: ', values);
Question: How, when i will click on that button to get data as base64?demo: https://codesandbox.io/s/silent-grass-sqzl1?file=/index.js:0-2944

Related

onRemove Upload Image antDesign + delete item from interface

I have a project, and this project contains several interfaces, and among these interfaces there is an interface for uploading an image, and the problem is in the deletion icon. When you click on it, a modal appears, but the element is deleted before the modal appears.
How can i solve the problem?
this file display a list of instructions that contains upload Image
import '../../../styles/input/index.scss';
import '../../../styles/dropzone/index.scss';
import { Button, Col, message, Modal, Row, Spin, Upload, UploadFile } from 'antd';
import { FunctionComponent, useCallback, useRef, useState } from 'react';
import { motion, useAnimation } from 'framer-motion';
import { defaultTranstion } from '../../../constants/framer';
import { Controller } from 'react-hook-form';
import FromElemnetWrapper from '../form-element-wrapper';
import { Delete, UploadCloud } from 'react-feather';
import { getBase64 } from '../../../utils/get-base64';
import _ from 'lodash';
import config from '../../../api/nuclearMedicineApi/config';
import { FormattedMessage } from 'react-intl';
import BasicModal from '../modal';
import { UploadOutlined } from '#ant-design/icons';
import axios from 'axios';
import { IFormError } from '../general-form-containner';
interface DropzoneProps {
name: string;
control: any;
rules?: any;
label: string;
disabled?: boolean;
multiple?: boolean;
accept?: string;
refType?: number;
defaultFileList?: any;
onRemove?: any;
customRequest?: (option: any) => void;
onProgress?: any;
}
const Dropzone: FunctionComponent<DropzoneProps> = ({
name,
control,
rules,
label,
disabled,
multiple,
accept,
refType,
defaultFileList,
onRemove,
customRequest,
onProgress
}) => {
const focusController = useAnimation();
const errorController = useAnimation();
const [previewVisible, setpreviewVisible] = useState(false);
const [previewImage, setpreviewImage] = useState('');
const handleCancel = () => setpreviewVisible(false);
const handlePreview = async (file: any) => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj);
}
setpreviewImage(file?.preview ?? file.url);
setpreviewVisible(true);
};
const [isModalOpen, setIsModalOpen] = useState(false);
const [errors, setErrors] = useState<IFormError[]>([]);
const [visibleModal, setVisibleModal] = useState(false);
const [removePromise, setRemovePromise] = useState();
const [deleteVisible, setdeleteVisible] = useState<boolean>(false);
const onDeleteHandle = () => {
setdeleteVisible(false);
};
const deletehandleCancel = () => {
setdeleteVisible(false);
};
let resolvePromiseRef = useRef<((value: boolean) => void) | undefined>();
// let resolvePromiseRef = useRef<HTMLInputElement | null>(null)
const handleRemove = useCallback(() =>{
const promise = new Promise(resolve => {
resolvePromiseRef.current = resolve
});
setVisibleModal(true);
return promise;
}, [])
const handleOkModalRemove = useCallback(() => {
if (resolvePromiseRef.current) {
resolvePromiseRef.current(true)
}
}, [removePromise]);
const handleCancelModalRemove = useCallback(() => {
if (resolvePromiseRef.current) {
resolvePromiseRef.current(false);
setVisibleModal(false)
}
}, [removePromise]);
return (
<>
<FromElemnetWrapper
focusController={focusController}
errorController={errorController}
label={label}
required={rules.required?.value}
>
<Controller
control={control}
name={name}
rules={rules}
render={({
field: { onChange, onBlur, value, name, ref },
fieldState: { invalid, error },
}) => {
if (invalid) {
errorController.start({ scale: 80 });
} else {
errorController.start(
{ scale: 0 },
{ ease: defaultTranstion.ease.reverse() },
);
}
return (
<div
onBlur={() => {
onBlur();
focusController.start({ scale: 0 });
}}
onFocus={() => {
focusController.start({ scale: 80 });
}}
className='relative'
>
<div className='upload-container'>
<form
className='dropzone needsclick'
id='demo-upload'
action='/upload'
>
{/* <> */}
<Upload
action={`${config.baseUrl}api/services/app/Attachment/Upload`}
headers={config.headers}
ref={ref}
multiple={multiple}
disabled={disabled}
data={{ RefType: refType }}
listType='picture'
fileList={value}
id={name}
accept={accept}
onPreview={handlePreview}
onRemove={handleRemove}
iconRender={
() => {
return <Spin style={{ marginBottom: '12px', paddingBottom: '12px' }}></Spin>
}
}
progress={{
strokeWidth: 3,
strokeColor: {
"0%": "#f0f",
"100%": "#ff0"
},
style: { top: 12 }
}}
beforeUpload={
(file) => {
console.log({ file });
return true
}
}
// onProgress= {(event: any) => (event.loaded / event.total) * 100}
// onChange={(e) =>
// onChange(e.fileList)
// }
// onChange={(response) => {
// console.log('response: ', response);
// if (response.file.status !== 'uploading') {
// console.log(response.file, response.fileList);
// }
// if (response.file.status === 'done') {
// message.success(`${response.file.name}
// file uploaded successfully`);
// } else if (response.file.status === 'error') {
// message.error(`${response.file.name}
// file upload failed.`);
// }
// else if (response.file.status === 'removed') {
// message.error(`${response.file.name}
// file upload removed.`);
// }
// }}
>
<div className='upload-button'>
<div className='wrapper'>
<motion.div
className='fas fa-angle-double-up'
whileHover={{
y: [
0, -2, 2,
0,
],
transition: {
duration: 1.5,
ease: 'easeInOut',
yoyo: Infinity,
},
}}
>
<UploadCloud
style={{
margin: '.2rem',
display:
'inline-block',
}}
color='white'
size={20}
/>
Upload
</motion.div>
</div>
</div>
</Upload>
{/*
<Modal
title="Are you sure?"
visible={visibleModal}
onOk={handleOkModalRemove}
onCancel={handleCancelModalRemove}
/> */}
<BasicModal
header={
<>
<FormattedMessage id={'confirmdeletion'} />
</>
}
headerType='error'
content={
<>
<Row>
<Col span={8} offset={4}>
<Button
type='primary'
className='savebtn'
onClick={onDeleteHandle}
style={{
cursor:
Object.keys(errors).length !==
0
? 'not-allowed'
: 'pointer',
}}
>
<FormattedMessage id={'affirmation'} />
</Button>
</Col>
<Col span={8} offset={4}>
<Button
type='default'
className='savebtn'
onClick={deletehandleCancel}
style={{
cursor:
Object.keys(errors).length !==
0
? 'not-allowed'
: 'pointer',
}}
>
<FormattedMessage id={'cancel'} />
</Button>
</Col>
</Row>
</>
}
isOpen={visibleModal}
footer={false}
width='35vw'
handleCancel={handleCancelModalRemove}
handleOk={handleOkModalRemove}
/>
{/* {_.isEmpty(value) && (
<div className='dz-message needsclick'>
<FormattedMessage id='dropfileshere' />
</div>
)} */}
<BasicModal
isOpen={previewVisible}
header={<FormattedMessage id="Preview image" />}
footer={false}
handleCancel={handleCancel}
content={<img
alt='example'
style={{ width: '100%' }}
src={previewImage}
/>}
/>
{/* </> */}
</form>
</div>
{invalid && (
<p className='form-element-error'>
{error?.message}
</p>
)}
</div>
);
}}
/>
</FromElemnetWrapper>
</>
);
};
export default Dropzone;
Why it doesn't work?
https://ant.design/components/upload
onRemove - A callback function, will be executed when removing file button is clicked, remove event will be prevented when return value is false or a Promise which resolve(false) or reject
You have to return a promise which resolves to false or return false
How to solve it?
You have to return a promise so first of all, you need a ref or a state to store resolve function of that promise so you can call it in modal
const resolvePromiseRef = useRef<((value: boolean) => void) | undefined>();
Then you will need to assign the resolve function to the ref and return the promise
Now onRemove will wait for your promise to resolve
const handleRemove = () =>{
const promise = new Promise(resolve => {
resolvePromiseRef.current = resolve
});
setVisibleModal(true);
return promise;
}
Now your functions handlers
const handleOkModalRemove = useCallback(() => {
if (resolvePromiseRef.current) {
resolvePromiseRef.current(true)
}
}, [removePromise]);
const handleCancelModalRemove = useCallback(() => {
if (resolvePromiseRef.current) {
resolvePromiseRef.current(false)
}
}, [removePromise]);
You can also use the state, but I recommend ref because it doesn't rerender the component when changed.

Using ant design uploader to show a profile picture

I am using the ant design uploader to show the user profile picture in the dashboard page, In case a picture is not there in the database, then it must show the default upload button instead.
Below is an example ant design provides, how would I implement the login I've mentioned above.
import { Upload, message } from 'antd';
import { LoadingOutlined, PlusOutlined } from '#ant-design/icons';
function getBase64(img, callback) {
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result));
reader.readAsDataURL(img);
}
function beforeUpload(file) {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
message.error('You can only upload JPG/PNG file!');
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error('Image must smaller than 2MB!');
}
return isJpgOrPng && isLt2M;
}
class Avatar extends React.Component {
state = {
loading: false,
};
handleChange = info => {
if (info.file.status === 'uploading') {
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
// Get this url from response in real world.
getBase64(info.file.originFileObj, imageUrl =>
this.setState({
imageUrl,
loading: false,
}),
);
}
};
render() {
const { loading, imageUrl } = this.state;
const uploadButton = (
<div>
{loading ? <LoadingOutlined /> : <PlusOutlined />}
<div style={{ marginTop: 8 }}>Upload</div>
</div>
);
return (
<Upload
name="avatar"
listType="picture-card"
className="avatar-uploader"
showUploadList={false}
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
beforeUpload={beforeUpload}
onChange={this.handleChange}
>
{imageUrl ? <img src={imageUrl} alt="avatar" style={{ width: '100%' }} /> : uploadButton}
</Upload>
);
}
}
ReactDOM.render(<Avatar />, mountNode);

set function does not trigger an update in React with Recoil

This seems like it should be easy but why isn't a button callback function with a setState call not triggering a refresh of the data item? Actually it's just the computeSMA button that isn't changing the sma when the button is selected. The other two callbacks to set inputs work. The fetchData updates the charts so i can't figure this out!! Must be too tired ...
import React, { useState, useEffect } from "react"
import { useRecoilState } from "recoil";
import { closingDataAtom, metaDataAtom, tickerAtom, timeSeriesAtom , smaAtom} from '../../utils/atoms'
import { Container } from '#material-ui/core'
import '../../utils/Home.css'
import { VictoryChart, VictoryBar, VictoryTheme, VictoryVoronoiContainer, VictoryLine, VictoryBrushContainer, VictoryZoomContainer } from 'victory';
import { Chart, Axis, Tooltip, Line, Point } from "bizcharts";
import {XYPlot, LineSeries} from 'react-vis';
const APIKEY = 'demo'
const Home = () => {
const [apikey, setApiKey] = useState(APIKEY)
const [ticker, setTicker] = useRecoilState(tickerAtom);
const [metadata, setMetaData] = useRecoilState(metaDataAtom)
const [closingdata, setClosingData] = useRecoilState(closingDataAtom)
const [dates, setDates] = useRecoilState(timeSeriesAtom)
const [sma, setSMA] = useRecoilState(smaAtom)
const TIME_RESOLUTION = 'Daily'
var requestUrl
if (TIME_RESOLUTION === 'Daily') {
requestUrl = "https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol=" + ticker + "&outputsize=full&apikey=" + apikey
} else {
requestUrl = "https://www.alphavantage.co/query?function=TIME_SERIES_WEEKLY_ADJUSTED&symbol=" + ticker + "&outputsize=full&apikey=" + apikey;
}
const fetchData = async () => {
fetch(requestUrl)
.then(response => response.json())
.then(data => {
var closing_price = []
var metadata = []
var dat = []
Object.keys(data['Time Series (Daily)']).forEach((dateKey) => {
closing_price.push(data['Time Series (Daily)'][dateKey]['5. adjusted close'])
dat.push({ 'date': new Date(dateKey) })
})
Object.keys(data['Meta Data']).forEach((metaKey) => {
metadata.push(data['Meta Data'][metaKey])
})
setDates(dat.reverse())
setClosingData(closing_price.reverse())
setMetaData(metadata)
})
.catch(e => {
});
};
const handleSMACompute = (e) => {
var sm = ['2', '3', '4']
setSMA(sm) <====== REACT IS NOT "REACTING"
}
const handleTickerInput = (e) => {
setTicker(e.target.value)
}
const handleAPIInput = (e) => {
setApiKey(e.target.value)
}
return (
<>
<Container className="container" maxWidth="sm">
<div>
<label>Ticker:</label> {ticker}
<input type="text" name="ticker" onChange={handleTickerInput} />
</div>
<div>
<label>APIKEY:</label> {apikey}
<input type="text" name="apikey" onChange={handleAPIInput} />
</div>
<button onClick={fetchData}>
Click it
</button>
<Container className="container" maxWidth="sm">
<ul>{metadata}</ul>
</Container>
<button OnClick={handleSMACompute}> Generate SMA </button>
<Container className="container" maxWidth="sm">
<ul>The value is {sma}</ul>
</Container><div>
</div>
<VictoryChart
theme={VictoryTheme.material}
domainPadding={10}
>
<VictoryBar
style={{ data: { fill: "#c43a31" } }}
data={closingdata}
/>
</VictoryChart>
<div>
<VictoryChart
theme={VictoryTheme.material}
>
<VictoryLine
style={{
data: { stroke: "#c43a31" },
parent: { border: "1px solid #ccc" }
}}
animate={{
duration: 20,
onLoad: { duration: 20 }
}}
containerComponent={<VictoryZoomContainer zoomDomain={{x: [5, 35], y: [0, 100]}}/>}
categories={{
y: dates
}}
data={closingdata}
/>
</VictoryChart>
</div>
</Container>
</>
);
}```
It seems to have been the button setup. I changed to this and it works....??ggrrrr
Click it
</button>
<Container className="container" maxWidth="sm">
<li>{metadata}</li>
</Container>
<button onClick={computeSMA}>
Click it
</button>
<Container className="container" maxWidth="sm">
<li>{sma}</li>
</Container>
In your first code, you used OnClick as the event name. Should be onClick. It is a react syntax and it is case sensitive.

Cant send photo from React frontend to express backend after build (create-react-app)

Ok so i have an MERN app (CRA) and photo upload. Everything works great but when i will do npm run build i cant send photo (over 1MB) to backend. I've used postman to test and again worked great :). In development mode its working. Something is blocking requests from frontend when i want to upload photos. I've checked logs with morgan and it shows that this request with large photo upload didnt happen. Im using axios to communicate with backend and express file upload but backend works well. I dont know what is blocking my photos. Also express file upload in debuging mode is telling that "Request is not eligible for upload!"
action in redux:
export const addGalleryPhoto = (file) => async (dispatch) => {
const config = {
headers: {
"Content-Type": "multipart/form-data",
},
};
try {
await axios.put(`/api/v1/config/uploadGalleryPhoto`, file, config);
dispatch(getPhotos());
} catch (err) {
const errors = err.response.data.error.split(",");
if (errors) {
errors.forEach((error) => dispatch(setAlert(error, "error")));
}
dispatch({ LOAD_PHOTOS_FAILED });
}
};
controller in node:
exports.uploadGalleryPhoto = asyncHandler(async (req, res, next) => {
if (!req.files) {
return next(new ErrorResponse(`Dodaj plik`, 400));
}
const file = req.files.file;
// Make sure the image is a photo
if (!file.mimetype.startsWith("image")) {
return next(new ErrorResponse(`Możesz wysłać tylko zdjęcia`, 400));
}
if (file.size > process.env.MAX_FILE_UPLOAD) {
return next(
new ErrorResponse(
`Zbyt duże zdjęcie. Maksymalny rozmiar pliku: ${Math.round(
process.env.MAX_FILE_UPLOAD / 1024
)} MB`,
400
)
);
}
file.name = `photo_${uuid()}${path.parse(file.name).ext}`;
file.mv(`${process.env.FILE_UPLOAD_PATH}/${file.name}`, async (err) => {
if (err) {
console.error(err);
return next(new ErrorResponse(`Problem with file upload`, 500));
}
const config = await Config.find();
await Config.findByIdAndUpdate(config[0].id, {
galleryPhotos: [...config[0].galleryPhotos, file.name],
});
res.status(200).json({
success: true,
data: file.name,
});
});
});
Gallery component:
import React, { useEffect, useState, useRef, Fragment } from "react";
import PropTypes from "prop-types";
import { Card, Modal, Button } from "antd";
import { DeleteOutlined } from "#ant-design/icons";
import Spinner from "./../layout/Spinner";
import { connect } from "react-redux";
import {
addGalleryPhoto,
getPhotos,
deletePhoto
} from "./../../store/actions/gallery";
const AdminGallery = ({ photos, addGalleryPhoto, getPhotos, deletePhoto }) => {
useEffect(() => {
getPhotos();
}, [getPhotos]);
const fileInput = useRef();
const [value, setValue] = useState("");
const [formData, setFormData] = useState({
deleteVisible: false,
photo: null
});
const onSubmit = e => {
e.preventDefault();
const formData = new FormData();
formData.append("file", value);
addGalleryPhoto(formData);
setValue("");
};
if (photos.loading || photos.photos.null) {
return <Spinner />;
} else {
return (
<Fragment>
<div className="admin-gallery">
<div>
<input
type="file"
style={{ display: "none" }}
ref={fileInput}
onChange={e => setValue(e.target.files[0])}
/>
{value === "" ? (
<button
type="button"
className="btn btn--primary"
onClick={e => fileInput.current.click()}
>
Dodaj plik
</button>
) : (
<button
type="button"
className="btn btn--primary"
onClick={e => onSubmit(e)}
>
Wyślij zdjęcie
</button>
)}
<div>
{photos.photos.length === 0 ? (
<div className="no-content">Brak Zdjęć</div>
) : (
<div className="admin-gallery__photo-wrapper">
{photos.photos.map(item => {
return (
<Card
style={{ padding: "0 !important" }}
key={item}
actions={[
<DeleteOutlined
onClick={() =>
setFormData({
...formData,
deleteVisible: true,
photo: item
})
}
/>
]}
>
<img
className="admin-gallery__photo"
src={`${process.env.PUBLIC_URL}/uploads/${item}`}
alt="photo"
/>
</Card>
);
})}
</div>
)}
</div>
</div>
</div>
{formData.deleteVisible && formData.photo !== null ? (
<Modal
visible={formData.deleteVisible}
title="Kasowanie zdjęcia"
onOk={() => {
deletePhoto(formData.photo);
setFormData({ ...formData, deleteVisible: false, photo: null });
}}
onCancel={() =>
setFormData({ ...formData, deleteVisible: false, photo: null })
}
footer={[
<Button
key="back"
onClick={() =>
setFormData({
...formData,
deleteVisible: false,
photo: null
})
}
>
Zamknij
</Button>,
<Button
key="accept"
onClick={() => {
deletePhoto(formData.photo);
setFormData({
...formData,
deleteVisible: false,
photo: null
});
}}
>
Skasuj
</Button>
]}
>
<p>Na pewno chcesz skasować to zdjęcie?</p>
</Modal>
) : (
""
)}
</Fragment>
);
}
};
AdminGallery.propTypes = {
photos: PropTypes.object.isRequired,
getPhotos: PropTypes.func.isRequired,
addGalleryPhoto: PropTypes.func.isRequired,
deletePhoto: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
photos: state.photos
});
export default connect(mapStateToProps, {
addGalleryPhoto,
getPhotos,
deletePhoto
})(AdminGallery);
Ok guys i've found an issue. I have nginx on my droplet and default file size to upload in nginx is set to 1mb. This line in nginx conf will do the job client_max_body_size 100M; Of course 100M is for example.

How to upload files using React?

so currently I am trying to upload a file, and save it on the Google Clouds. But I am stuck at how to get the input file using React-Redux.
Basically, my back-end part already finish, I tested it using HTTPie with this command
HTTP localhost:8000/v1/... #(file_path)
and it works perfectly fine.
Now after I use input tag:
<input type="file" onChange="">
I do not know how to get the file_path that are the user choose.
And I dont even know whether I can get the uploaded file using this method.
Thank you
You cant handle onChange file by method <input type="file" onChange={this.handleChangeFile.bind(this)}>
handleChangeFile(event) {
const file = event.target.files[0];
let formData = new FormData();
formData.append('file', file);
//Make a request to server and send formData
}
First : Create a component file name 'FileUpload.js'
import React, { Fragment, useState, useEffect } from 'react'
function FileUpload (props) {
useEffect(() => {
clearFileUpload()
}, [])
const [fileName, setFileName] = useState('')
const [fileSize, setFileSize] = useState('')
const [fileSizeKB, setFileSizeKB] = useState('')
const [fileType, setFileType] = useState('')
const [src, setSrc] = useState('')
const clearFileUpload = () => {
setFileName('')
setFileSize('')
setFileType('')
setSrc('')
props.dataChanger('')
}
const onPickFile = (e) => {
e.preventDefault();
clearFileUpload()
document.getElementById(props?.name).click()
}
const onFilePicked = (e) => {
let files = e.target.files;
let file_name = files[0].name;
let file_size = getFileSize(files[0].size);
let file_size_kb = getFileSizeKB(files[0].size);
let file_type = getFileType(files[0]).toLowerCase();
setFileName(file_name)
setFileSize(file_size)
setFileSizeKB(file_size_kb)
setFileType(file_type)
if (props?.max_file_size_in_kb && file_size_kb > props?.max_file_size_in_kb)
{
alert('Maximum allowed file size = '+props?.max_file_size_in_kb+ ' kb')
clearFileUpload()
return false;
}
if (props?.allowed_extensions && !arrToLowerCase(props?.allowed_extensions).includes(file_type))
{
clearFileUpload()
alert('Allowed file type = '+props?.allowed_extensions)
return false;
}
let fileReader = new FileReader();
fileReader.addEventListener('load', ()=>{
// console.log(fileReader.result);
props.dataChanger(fileReader.result)
setSrc(fileReader.result)
})
fileReader.readAsDataURL(files[0])
}
const getFileSize = (file_size) =>
{
if ( (file_size/1024) >= 1024 )
{
file_size= parseInt((file_size/1024)/1024) + ' MB';
}
else{
file_size=parseInt(file_size/1024) + ' KB';
}
return file_size;
}
const getFileSizeKB = (file_size) =>
{
file_size=parseInt(file_size/1024);
return file_size;
}
const getFileType = (file) =>
{
return file?.type.split('/').pop();
}
const arrToLowerCase = (arr=[]) => {
return arr.map(str => str.toLowerCase());
}
return (
<Fragment>
<button className="btn btn-primary text-capitalize mr-2 mb-2" onClick={(e) => onPickFile(e)}>{props?.button_title || 'Upload File'}</button>
{(props?.required && fileName?.length<=3 && !src ) ? <label className="label label-danger">Required</label> : ''}
<br />
{fileName ? <label className="label label-primary">{fileName}</label> : ''}
{fileSize ? <label className="label label-info">{fileSize}</label> : ''}
<br />
{/* new upload file */}
{(props?.type=='image' && src && (props?.prev_src)) ? <img src={src} style={{ maxHeight: "150px", maxWidth: "150px" }} alt="" className="mt-2" /> : ''}
{/* previous image */}
{(props?.type=='image' && (props?.prev_src) && !src) ? <img src={props?.prev_src} style={{ maxHeight: "150px", maxWidth: "150px" }} alt="" className="mt-2" /> : ''}
{(props?.type=='image' && src && (props?.prev_src)) ? <button className="btn btn-danger btn-outline-danger pl-1 pr-0 py-0 ml-2" onClick={clearFileUpload} title="Remove file"><i className="icofont icofont-ui-close"></i></button> : ''}
<input className='file d-none' type="file" data-show-upload="true" data-show-caption="true"
id={props?.name}
name={props?.name}
required={props?.required ? true : false}
onChange={(e) => onFilePicked(e)}
/>
</Fragment>
)
}
export default FileUpload
Second : How to use this component in your form
import FileUpload from './FileUpload.js'
<FileUpload
name="thumbImage"
button_title="Thumbnail Image Upload"
max_file_size_in_kb="200"
dataChanger={(value) => dataChangerThumbnail(value)}
type="image"
prev_src={'localhost:8001/'+formData?.thumbImage}
required
allowed_extensions={[ 'jpg', 'jpeg', 'png', 'gif']}
/>
const dataChangerThumbnail = (value) => {
setFormData({...formData, thumbImage: value})
}
const formInitial = {
thumbImage: '',
}
const [formData, setFormData] = useState(formInitial)
Checkout this blog, very well explained upload feature in react with example.
Below is upload code from above blog:
handleUploadedFiles = files => {
var reader = new FileReader();
reader.onload = (e) => {
//Split csv file data by new line so that we can skip first row which is header
let jsonData = reader.result.split('\n');
let data = [];
jsonData.forEach((element, index) => {
if(index) {
//Split csv file data by comma so that we will have column data
const elementRaw = element.split(',');
console.log(element, index);
if(element) {
let param = {
'id' : elementRaw[0],
'name' : elementRaw[1],
'age' : elementRaw[2],
'address' : elementRaw[3]
}
data.push(param);
}
}
});
}
console.log("TCL: Dashboard -> reader.readyState", reader.readyState)
reader.readAsText(files[0]);
}
Complete Example with AXIOS and FORMIK
import "./App.css";
import { useEffect, useState } from "react";
import * as Yup from "yup";
import { Formik, Field, Form, ErrorMessage, useField } from "formik";
import axios from "axios";
function App() {
return (
<Formik
initialValues={{
profile: [],
}}
validationSchema={Yup.object({
profile:Yup.array().min(1,"select at least 1 file")
})}
onSubmit={(values, props) => {
let data = new FormData();
values.profile.forEach((photo, index) => {
data.append(`photo${index}`, values.profile[index]);
});
axios
.post("you_api_for_file_upload", data, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then((response) => {
console.log(response);
})
.catch((err) => {
console.log(err);
});
}}
>
{(formik) => {
return (
<>
<Form>
<input
id="file"
name="profile"
type="file"
onChange={(event) => {
const files = event.target.files;
let myFiles =Array.from(files);
formik.setFieldValue("profile", myFiles);
}}
multiple
/>
<ErrorMessage name="profile"/>
<button type="submit" disabled={formik.isSubmitting}>
Submit
</button>
</Form>
</>
);
}}
</Formik>
);
}
export default App;
Hope this will help you

Resources