AntD's Upload keeps showing failed tooltip but it is uploading successfully - reactjs

I am using antd's Upload component, its task is to just upload the image and then I grab that image and send it to the API to store it. But I keep getting upload failed message tooltip as I am not using any action prop that they provide. Even their own website has this problem as I'm trying to upload something and it shows failed message but it has been actually uploaded. antd's Upload
I am using useState to save the file const [uploadedImage, setUploadedImage] = useState();
My fileProps looks like this:
const fileProps = {
name: 'file',
multiple: false,
onChange(info) {
if (info.file.status !== 'uploading') {
let reader = new FileReader();
reader.onload = (e) => {
setData({
...data,
image: new File([e.target.result], info.file.name),
});
setIsFileUploaded(true);
}
reader.readAsDataURL(info.file.originFileObj);
setUploadedImage(info.file.originFileObj);
}
},
};
I then pass it to the Upload Component:
<Upload {...fileProps}>
<Button icon={<UploadOutlined />}>Upload Image</Button>
</Upload>
Why does it keep showing Upload error Tooltip even though it is successfully uploading and I can store it? how can I remove this tooltip? I know there is a way to hide the list entirely by using: showUploadList: false but I want to show the uploaded file as sometimes during big uploads I don't have any sort of confirmation if the file is uploading or uploaded.
I have also created codesandbox for it: https://codesandbox.io/s/bold-bash-g3qkj

If you just want to save the file to the state, and not send it automatically to the server, you must set the property beforeUpload.
const fileProps = {
name: "file",
multiple: false,
beforeUpload: () => {
return false;
},
onChange(info) {
if (info.file.status !== "uploading") {
let reader = new FileReader();
reader.readAsDataURL(info.file);
setUploadedImage(info.file);
}
}
};

Related

Reading multiple files using FileRead() and adding them on React State

So I'm building an app that allows you to chose more than one photo, on chose I set the files in React State then i listed for change for that state with useEffect so I can iterate and convert to base64 using FileRead to preview what I've uploaded. But I'm having a problem that the data I'm getting is weird, some of the files are read and added to the React State and some just appear just as File, here is the screenshot:
Here is the screenshot of the Console Log (can't add the object because is to long)
And here is how I add to the state the files on upload:
<input
className={styles.hidden_input}
type='file'
multiple='multiple'
accept='image/*'
onChange={(event) => {
const files = event.target.files;
if (files) setImages(files);
else setImages(null);
}}
And here is how I convert them when uploads are made:
useEffect(() => {
if (images.length !== 0) {
for (let i = 0; i < images.length; i++) {
let file = images[i];
const reader = new FileReader();
reader.onload = () => {
const single = reader.result;
setImagesStream([...images, single]);
};
reader.readAsDataURL(file);
}
} else {
console.log('No images where found.');
}
}, [images]);
When I try to iterate, just the last image shows the other show blank, because aren't converted.
You need to pass a function to the setState of 'setImagesStream' so it can read from the current value in the updater instead of reading the current value rendered. Here is the docs reference
Something like this should work:
setImagesStream((state) => {
return [...state, single]
});

How to replace a loading image in a chat with an image uploaded to Firebase Storage using ReactJS?

I have built a chat using Firebase and ReactJS. I mainly followed their Firebase's web codelab at https://firebase.google.com/codelabs/firebase-web#1. However, I have gotten stuck on the image uploading functionality. Since I am using ReactJS, I have had to modify their plain JS code to match mine. I am able to save a message with a "loading" image url in Firestore, then, I successfully save the image that I want to ultimately show in the chat in Firebase Storage, and finally then I successfully retrieve its url from Storage and replace it with the url of the loading image in Firestore. The image does show in the chat, however, the loading image is not actually replaced but, instead, it remains in the chat when I want it to be completely replaced, obviously, so that the loading image is no longer there. Here's what I mean, in this image:
As you can see the loading image on top stayed on instead of being replaced by the image underneath it. I think it should be filtered out somehow before I save the new snapshot with the new image url. However, I can not figure out how to do it correctly. I tried to filter it out based on the url of the loading image which is saved locally but since it is saved as a base64 in Storage, it did not work. Neither did using the actual Base64 code as a way to filter it out. So, I need help to solve this issue. The codelab does not really specify this nor is it clear how they do it in their code which is in plain Javascript anyways and I use ReactJS so it may not be 100% suitable.
Here's, I believe, enough code to see what is going on. Let me know if you need more of it.
Here's how I send images to the Chat: (modeled on the Firebase codelab)
sendImageToChat () {
this.state.chatFiles.forEach((file) => {
firebase.firestore().collection('Chats')
.doc(this.state.uid)
.collection('Messages')
.add({
docId: this.state.docId,
imageUrl: loadingLogo,
timestamp: new Date(),
uid: this.state.uid,
name: this.state.displayName,
email: this.state.email
})
.catch((error) => {
this.setState({ writeError: error.message });
})
.then((messageRef) => {
// 2 - Upload the image to Cloud Storage.
const filePath = `users/${this.state.displayName}/${this.state.uid}/${moment().format("MMM Do YY")}/${uuidv4()}/${file.name}`
return firebase.storage().ref(filePath).put(file).then((fileSnapshot) => {
// 3 - Generate a public URL for the file.
return fileSnapshot.ref.getDownloadURL().then((url) => {
// 4 - Update the chat message placeholder with the image's URL.
return messageRef.update({
imageUrl: url,
storageUri: fileSnapshot.metadata.fullPath
});
});
});
}).catch(function(error) {
console.error('There was an error uploading a file to Cloud Storage:', error);
});
})
this.setState({
chatFiles: []
})
document.getElementById('file-1').value = "";
}
Here's how I, then, setState when the loading image is added and then when its url is modified: (Notice how I try to filter out the loadingLogo which is the loading image out of the state but it does not obviously work for the reason explained above).
startChat () {
document.getElementById("myForm").style.display = "block";
const ref = firebase.firestore().collection('Chats').doc(this.state.uid).collection('Messages');
const query = ref.orderBy('timestamp', 'desc').limit(10)
this.unsubFromMessages = query.onSnapshot((snapshot) => {
if (snapshot.empty) {
console.log('No matching documents.');
firebase.firestore().collection('Chats').doc(this.state.uid).
set({
name: this.state.displayName,
uid: this.state.uid,
email: this.state.email
}).then(console.log("info saved"))
.catch((error) => {
console.log("Error saving info to document: ", error);
});
}
snapshot.docChanges().reverse().forEach((change) => {
if (change.type === 'removed') {
console.log(change.doc.data().content)
} else if (change.type === 'added') {
this.setState(state => {
const messages = [...state.messages, {id: change.doc.id, body: change.doc.data()}]
return {
messages
}
})
setTimeout( this.scrollToBottom(), 2000)
} else if (change.type === 'modified') {
const filteredMessages = this.state.messages.filter(message => message.imageUrl !== loadingLogo)
console.log(filteredMessages)
this.setState(state => {
const messages = [...filteredMessages, {id: change.doc.id, body: change.doc.data()}]
return {
messages
}
})
setTimeout( this.scrollToBottom(), 2000)
}
});
}, (error) => {console.log(error)});
}
This is part of the Chat's JSX:
<div className="chatArea" id='messages'>
{
this.state.messages.map((message, index) => {
return message.body.uid === this.state.uid
?
<div>
{
message.body.imageUrl ?
<img src={message.body.imageUrl} className="message-sent"></img>
:
<p className="message-sent" key={index}>{message.body.content}</p>
}
</div>
:
<p className="message-received" key={index}>{message.body.content}</p>
})
}
<div style={{ float:"left", clear: "both" }}
ref={(el) => { this.myRef = el; }}>
</div>
</div>
I know the issue is not with Firebase but rather with ReactJS. I know I need to remove, filter out, replace or delete that loading image before or after the modified message with the new url is saved to the state. So, please help me figure this out. I am sure many people may encounter this problem.
Thank you!
I figured it out. I might as well delete this question but it may help someone build a chat with ReactJS and Firebase. Anyways, my approach to filter out based on the object property, imageUrl is a viable option. It works! My silly oversight was that I did not add the parent property or object, "body", after the object "message". More specifically, instead of const filteredMessages = this.state.messages.filter(message => message.imageUrl !== loadingLogo), it should be const filteredMessages = this.state.messages.filter(message => message.body.imageUrl !== loadingLogo). You can also try to add an object property that you can use to filter out messages with, for example, allowed: yes or no. If you need more clarification, just ask me, I am glad to help. Happy coding!

What is the Syntax for displaying a State img Url in React?

I think I have a simple question to which I haven't found an answer which would work for me.
The main idea is: Once the User uploads the image to aws s3 it would get displayed (the reading from DB still in progress, but it would work as below +/-).
Why isn't the below img not displaying in React, if I set it manually to it it works. :
handleFiles = async (e) => {
const uploadedImage = await uploadFile(e.target.files[0]);
this.setState({imgLink:uploadedImage})
};
state = {
imgLink: "../../../images/person-01.jpg",
};
render() {
.....
<div className="bg-transfer">
<img src={this.state.imgLink} alt="" />
</div>
.....
<div className="single-file-input">
<input
type="file"
id="user_image"
name="user_image"
onChange={this.handleFiles}
/>
Upload file:
import S3FileUpload from "react-s3";
const ID = "";
const SECRET = "";
//If bucket not set, getting 400 error.
const BUCKET_NAME = "";
//If Region not set then this isn't working, getting 400 error
const REGION_NAME = "";
const config = {
bucketName: BUCKET_NAME,
/*dirName: 'media', /* optional */
region: REGION_NAME,
accessKeyId: ID,
secretAccessKey: SECRET,
/*s3Url: 'https:/your-custom-s3-url.com/', /* optional */
};
export const uploadFile = async (file) => {
try {
const response = await S3FileUpload.uploadFile(file, config)
return response.location;
} catch (error) {
console.log(error);
}
};
I tried to use imgLink: "\"../../../images/person-01.jpg\"",, as I thought I am missing links, but it didn't work either. Since I will be reading the path from MongoDB I can't simply import the image, but would require this to be dynamically changed.
What syntax should be used here?
uploadedImaged is probably not a valid url link, unless the function uploadFile returns the url generated for the file. You should be update the state with the url for the file generated in s3.
I am not quite sure why I can't use the state for the img URL still, but turns out that if you take the URL from MongoDb directly and place in the HTML, it is displayed. Which is what I wanted at first.
This time the middle developing step was not necessary.

Passing correct properties to popup notification

I want to show a notification with the upload status. I took over a project in React & ASP.NET and I am relatively new to this. The question is quite simple, yet I am struggling to solve it: How do I display a popup notification showing which files have been successfully been uploaded and which not?
import * as React from "react";
import { connect } from "react-redux";
import { Form, Select, Button, Upload, message, notification} from 'antd';
import * as Actions from "../actions";
const FormItem = Form.Item;
class UploadFileForm extends React.Component<any, any> {
constructor(props: any) {
super(props);
}
handleSubmit = (e) => {
message.config({ top: 0 });
message.loading('Importing in progress...', 3);
e.preventDefault();
this.props.uploadFile(this.props.form.getFieldsValue());
notification["info"]({
message: 'Files successfully uploaded',
description: '', // <-- this line has to be modified
duration: 10
});
}
render() {
const { getFieldDecorator } = this.props.form;
return (
<Form onSubmit={this.handleSubmit}>
<FormItem label="File" >
{getFieldDecorator('upload', {
valuePropName: 'fileList',
getValueFromEvent: (e) => e.fileList.slice(-1)
})(
<Upload name="importFile" action={' '} multiple={false}>
<Button> Upload </Button>
</Upload>
)}
</FormItem>
<Button type="primary" htmlType="submit">Import</Button>
</Form>
);
}
}
export default Form.create()(UploadFileForm);
More specifically: How do I have to modify the line description: '', to show me a list of all uploaded files and their status as pure text, e.g. File(s) '1.txt', '2.txt', and '3.txt' have been successfully uploaded. File(s) '4.txt' failed.?
The project documentation says that we are using Redux-Saga, but I am not so maybe that makes the story easier.
I guess your this.props.uploadFile method is a promise so considering that you should show notification once that promise is resolved
this.props.uploadFile(this.props.form.getFieldsValue()).then(result => {
// since your client doesn't know which ones are success/failed, server should return
// this information when request is finished
const { successUploads, failUploads } = result;
notification["info"]({
message: 'Files successfully uploaded',
description: `File(s) ${successUploads.join(', ')} have been successfully uploaded. File(s) ${failUploads.join(', ')} failed.`
duration: 10
});
});
If you can't control whats returned from the server then you'd need to track uploads on client side, but that would mean having multiple uploads (requests) to the server and your upload method would look something like this:
async function uploadFiles(files) {
// I've called your server upload uploadService.send(), but replace this with your method
const results = await Promise.all(
files.map(file => uploadService.send(file))
.map(p => p.catch(e => e)
);
let successUploads = [];
let failUploads = [];
results.forEach((result, idx) => {
const file = files[idx];
if (result instanceof Error) {
failUploads.push(file);
} else {
successUploads.push(file);
}
});
return {
successUploads,
failUploads
}
}
Then you could call uploadFiles same way as shown in first snippet.

Convert File type to Data URI - React-Dropzone

I am having trouble integrating React-dropzone with FeathersJS Upload
I have successfully implemented the RESTful upload when you POST a datauri to my Upload endpoint. { uri: data:image/gif;base64,........}
My issue is when selecting a file in react-dropzone and submitting the form, I am seeing a File type... It seem's I need to somehow convert that to a data URI.
This should be handled by Dauria... But I think my issue is in my POST request, not having the file property set with the correct file format. Should I be converting the File to FormData?
Here is one way to do it from File object:
Using Image and FileReader allows you to get width, height and base64 data:
onDrop = (acceptedFiles, rejectedFiles) => {
const file = acceptedFiles.find(f => f)
const i = new Image()
i.onload = () => {
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => {
console.log({
src: file.preview,
width: i.width,
height: i.height,
data: reader.result
})
}
}
i.src = file.preview
}

Resources