Hi im making a multi upload files button with Ant Design. My goal is update state fileList with the list of file and convert originFileObj into base64 string. The problem is my function only returns one base64 string for all files in fileList. Here is my code:
import React from 'react';
import { Upload } from 'antd';
export default class MultiUpload extends React.Component {
constructor(props: any) {
super(props);
this.state = {
fileList: []
};
this.handleUpload = this.handleUpload.bind(this);
}
handleUpload = (info: any) => {
let fileList = [...info.fileList];
// Accept 5 files only
fileList = fileList.slice(-5);
fileList.forEach(function(file, index) {
let reader = new FileReader();
reader.onload = (e) => {
file.base64 = e.target.result;
};
reader.readAsDataURL(info.file.originFileObj);
});
this.setState({fileList: fileList});
};
componentDidUpdate(prevProps, prevState) {
console.log(this.state)
}
render() {
return (
<div>
<Upload
listType={"text"}
multiple={true}
onChange={this.handleUpload}
>
<button >
Upload
</button>
</Upload>
</div>
)
}
};
onŠ”hange function is called with an object with such fields
{
file: {/ * ... * /},
fileList: [/ * ... * /],
event: {/ * ... * /},
}
You always pass only the current file into readAsDataURL. Instead, you need to pass a file from the fileList.
handleUpload function should look something like this:
handleUpload = (info: any) => {
let fileList = [...info.fileList];
// Accept 5 files only
fileList = fileList.slice(-5);
fileList.forEach(function (file, index) {
let reader = new FileReader();
reader.onload = (e) => {
file.base64 = e.target.result;
};
reader.readAsDataURL(file.originFileObj);
});
this.setState({ fileList });
};
Related
I added options to the existing toolbar in Gutenberg. I have a lot of options, so I thought that they would be automatically registered. The tasks are easy; every task has to call OpenAI based on the selected block, but every time it calls, it should use the latest text. I am not Frontend developer so I have little problem with understand where I made mistake, how it should be improve?
Component:
const AiBlockEdit = (props) => {
const [registered, _] = useState(() => {
let localRegistered = [];
FORMAT_LIST.forEach((format) => {
if (!localRegistered.includes(format)) {
let result = register(new OpenAI(globalOpenAi), wrappProps(format, props));
localRegistered.push(result);
}
});
return localRegistered;
});
return (
<>
<BlockControls>
<ToolbarDropdownMenu
icon={ AI }
label="Select a direction"
controls={ registered }
/>
</BlockControls>
</>
);
};
Logic:
export const wrappProps = (item, props) => {
return {
...item,
props: props
}
}
export function register(api, item) {
const {value, onChange} = item.props;
// Build message
let promptFormatType = new BuildPrompt()
.addText(item.input)
.addText(" ")
.addText(value.text)
.build();
// Prepare openai settings, use default
let requestParams = new OpenAIParams().setPrompt(promptFormatType).build();
let request = new item.Query(requestParams);
// Build settings
return new SettingsFormatType()
.setIcon(item.icon)
.setTitle(item.title)
.setOnclick(() => {
api.send(request, (response) => {
let text = response.data.choices[0].text;
onChange(insert(value, value.text + text));
// let xd = create({
// text: response.data.choices[0].text,
// })
// onChange(item.fn(value, xd));
});
}).build();
}
I tried to upload Multiple files and wants to read the content of the file for encrypt the data.
I can able to read the single file properly , but I can't do it while upload multiple files am getting error reader is busy.
If I create new Filereader while onloadend it gives me null value of content.
React JS - sample code:
let reader = new FileReader();
class FilReaderComp extends Component {
constructor(props) {
super(props);
this.state = {
}}
upLoadFileFolderFunc(e){
e.preventDefault();
let fileitemsList = e.target.files;
for (let i = 0; i < fileitemsList.length; i++) {
let fileitems = fileitemsList[i];
reader.onloadend = this.handleFileRead;
reader.readAsText(fileitems);
}
}
handleFileRead = (e) => {
const content = reader.result; here am reading content of the file
//here doing my function after getting content
}
render(){
return(
<input type="file" className="custom-file-input" style={{display:"hide"}}
onChange={this.upLoadFileFolderFunc} multiple/>
);}
export default withRouter(FilReaderComp);
To use multiple readers you have to create them in the for loop and access them via the event object(e.targrt) in the callback.
class FilReaderComp extends Component {
constructor(props) {
super(props);
this.state = {
}
}
upLoadFileFolderFunc(e){
e.preventDefault();
let fileitemsList = e.target.files;
for (let i = 0; i < fileitemsList.length; i++) {
let fileitems = fileitemsList[i];
let reader = new FileReader();
reader.onloadend = this.handleFileRead;
reader.readAsText(fileitems);
}
}
handleFileRead = (e) => {
const content = e.targrt.result; //here am reading content of the file
//here doing my function after getting content
}
render(){
return(
<input type="file" className="custom-file-input" style={{display:"hide"}}
onChange={this.upLoadFileFolderFunc} multiple/>
);
}
}
export default withRouter(FilReaderComp);
This is my simple code. And I am just rendering a single marker and try to click it.
But it is not clickable and nothing happens.
import React, { PureComponent } from 'react';
import ReactDOMServer from 'react-dom/server';
import * as PIXI from 'pixi.js';
import 'leaflet-pixi-overlay';
import L from 'leaflet';
import { SvgMarker } from '../../../utils/mapIcon';
const pixiMarkerContainer = new PIXI.Container();
let markerTextures = {};
class MapRouteMarkers extends PureComponent {
constructor(props) {
super(props);
this.state = { markerTexturesLoaded: false };
}
This is my markerOverlay, I have made it interactive by adding newMarker.interactive = true; and also registered click event on newMarker.
markerOverlay = L.pixiOverlay(utils => {
const map = utils.getMap();
const scale = utils.getScale();
const renderer = utils.getRenderer();
const container = utils.getContainer();
if (map && Object.keys(markerTextures).length !== 0) {
if (container.children.length) container.removeChildren();
const newMarker = new PIXI.Sprite(markerTextures.default);
const newMarkerPoint = utils.latLngToLayerPoint([50.63, 13.047]);
newMarker.x = newMarkerPoint.x;
newMarker.y = newMarkerPoint.y;
newMarker.scale.set(1 / scale);
newMarker.interactive = true;
newMarker.buttonMode = true;
newMarker.on('click', () => {
console.log('gggg');
});
container.addChild(newMarker);
renderer.render(container);
}
}, pixiMarkerContainer);
Here I am redrawing markers
componentDidUpdate() {
if (this.state.markerTexturesLoaded) {
const map = this.props.mapRef;
this.markerOverlay.addTo(map);
this.markerOverlay.redraw();
}
}
Here I am loading baseTexture from svg.
componentDidMount() {
if (Object.keys(markerTextures).length === 0) {
const loader = new PIXI.Loader();
const svgString = ReactDOMServer.renderToString(<SvgMarker iconColor="yellow" />);
const DOMURL = self.URL || self.webkitURL || self;
const img = new Image();
const svg = new Blob([svgString], { type: 'image/svg+xml;charset=utf-8' });
const url = DOMURL.createObjectURL(svg);
img.src = url;
const texture = new PIXI.Texture.from(img);
loader.load((thisLoader, resources) => {
markerTextures = { default: texture };
this.setState({
markerTexturesLoaded: true,
});
});
}
}
render() {
return null;
}
}
export default MapRouteMarkers;
here is the image attached for the same
Do anyone know what I am doing wrong here?
Try to handle the event differently, this worked for me:
newMarker.click = (event) => {
console.log(`Click, info=${JSON.stringify(event.data.global)}`);
};
You can also use the 'hover' and 'mouseout' events the same way for instance.
I'm using html fileinput to upload a file with reactjs, but once I uploaded a file, I cannot call the function to upload another file, unless I refresh the page of course.
A simplified version of my code would be:
class Matrice extends React.Component {
constructor(props) {
super(props);
this.fileInput = null;
}
uploadQuestion = async e => {
console.log("uploading question");
if (e.target.files[0]) {
const form = new FormData();
let type;
if (e.target.files[0].type == "image/jpeg") type = ".jpg";
if (e.target.files[0].type == "image/png") type = ".png";
if (e.target.files[0].type == "image/gif") type = ".gif";
// const fileName = this.props.current + type;
form.append("files", e.target.files[0]); //filename
form.append("ref", "exam"); // model
form.append("refId", this.props.match.params.id); // id
form.append("field", "media"); // name of field (image field)
this.setState({ questionUploadLoading: true });
const files = await strapi.upload(form);
this.saveMontage(files, undefined, "question");
}
};
render() {
return (
<>
<input
style={{ display: "none" }}
ref={fileInput => (this.fileInput = fileInput)}
onChange={this.uploadQuestion}
className="file"
type="file"
id="imgAdd"
/>
<button
onClick={() => this.fileInput.click()}
type="button"
className="btn btn-secondary"
>
<i className="fas fa-image" />
</button>
</>
);
}
}
But my function uploadQuestion cannot be called again once I finished uploading a file. Namely, the console.log('uploading question') doesn't show up (the second time).
I don't know what could be the reason, but I guess that something is preventing the onChange handler as if, uploading a file the second time doesn't "changes" the trigger.
Does anybody have an idea what could cause this?
Thanks
You can reset the file input by setting its value to the empty string, and you will be able to use it again.
uploadQuestion = async (e) => {
console.log('uploading question')
if (e.target.files[0]) {
// ...
this.fileInput.value = "";
}
}
You need to set the state for image that to be upload there is flow the step
Set a state for upload file in your Constructor (uploadFile:null)
Add a function for handle file Change
Use state upload(uploadFile) into uploadQuestion() instead of e.target.value[0]
After Upload setState back to uploadFile:null
set the file input onChange={this.fileHandle}
class Matrice extends React.Component {
constructor(props) {
super(props);
this.state:{
uploadFile:null
}
this.fileInput = null;
this.fileHandle = this.fileHandle.bind(this)
}
fileHandle (e, a) {
e.preventDefault()
this.setState({ upload: e.target.files[0] })
};
uploadQuestion = async (e) => {
console.log('uploading question')
if (e.target.files[0]) {
const form = new FormData();
let type;
if (e.target.files[0].type == 'image/jpeg') type = '.jpg'
if (e.target.files[0].type == 'image/png') type = '.png';
if (e.target.files[0].type == 'image/gif') type = '.gif';
// const fileName = this.props.current + type;
//Use state upload(uploadFile) into uploadQuestion() instead of e.target.value[0]
file.append('images', this.state.uploadFile, this.state.uploadFile.name) //filename
form.append('ref', 'exam'); // model
form.append('refId', this.props.match.params.id) // id
form.append('field', 'media') // name of field (image field)
this.setState({questionUploadLoading: true})
const files = await strapi.upload(form);
this.saveMontage(files, undefined, 'question')
//After Upload setState back to uploadFile:null
this.setState({uploadFile:null})
}
}
if you like to valid in onChange you can modify function as Below
fileHandle (e) {
e.preventDefault()
if (!e.target.files[0].name.match(/.(jpg|jpeg|png|gif)$/i)) {
this.setState({ errorMsg: 'Please upload valid file. Allowed format jpg, jpeg, png, gif' })
return false
} else {
this.setState({ upload: e.target.files[0], errorMsg: '' })
}
};
I had a heck of a time with this and no matter what I did from above nothing worked. Now, I've simply hardcoded the value to an empty string and I can upload over and over. I'm not even sure why this works, but I don't ever need the text value. The server cares about that. Here's a styled button using Material-UI where you never see the input, but you can upload over and over (in my case the server sends back some error and please fix your xlsx file message and I needed the user to be able to fix and try again):
import React from 'react';
import { Button } from '#material-ui/core';
import BackupIcon from '#material-ui/icons/Backup';
const UploadButton = ({ onChange, name, label, disabled }) => {
return (
<div className={'MuiFormControl-root MuiTextField-root'}>
<input
name={name}
id='contained-button-file'
type='file'
accept='.csv, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
style={{ display: 'none' }}
onChange={onChange}
disabled={disabled}
value=''
/>
<label htmlFor='contained-button-file'>
<Button
color='primary'
aria-label='Upload scan file.'
variant='contained'
component='span'
startIcon={<BackupIcon />}
disabled={disabled}
>
{label}
</Button>
</label>
</div>
);
};
export default UploadButton;
Just handle it using click event
const handleClick = event => {
const { target = {} } = event || {};
target.value = "";
};
<input type="file" onChange={handleChange} onClick={handleClick} />
How can i get data from input with type file and send it with axios in reactjs?
I found something about formData but i didn't find anything about get data from input and send it with axios.
thanks.
Lets assume that you have all the input data along with the file in your state like
constructor(props) {
super(props);
this.state = {
file : someName.txt, // file input
stateName : 'MP' // Text Input
date : 07/08/2018 // Date input
}
}
Now, in you handelSubmit method construct a JSON Object
handelSubmit = () => {
const { file, stateName, date } = this.state;
let data = [];
data['file'] = file;
data['stateName'] = stateName;
data['date'] = date;
// a function which makes a axios call to API.
uploadFile(data, (response) => {
// your code after API response
});
}
Here is a function to make a API call by axios
uploadFile(data, callback) {
const url = ''; // url to make a request
const request = axios.post(url, data);
request.then((response) => callback(response));
request.catch((err) => callback(err.response));
}
UPDATED :
Text On Change method to set state
handelOnChange = (event) => {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
}
Method on upload of file to set into state
handelOnUploadFile = (event) => {
this.setState({
file : event.target.files
})
}
Here is a JSX code.
render() {
return(
<div>
<input type="file" onChange={this.handelOnUploadFile} /> {/* input tag which to upload file */}
<input type="text" name="stateName" onChange={this.handelOnChange} /> {/* text input tag */}
<button type="submit" onClick={this.handelSubmit}> UPLOAD </button>
</div>
)
}
Hope it helps you.