I'm using react-dropzone to upload files to my server in my react app. Everything is working great but I want to add a feature where if a file is taking too long to upload due to its size, the user can cancel the process with the click of a button.
<Dropzone
multiple={ false }
accept={ allowedMimeTypes }
onDrop={ this.onDrop }
onDragEnter={ this.onDragEnter }
onDragLeave={ this.onDragLeave }
className={ classes.dropzone }
maxSize={ MAX_UPLOAD_BYTES }
>
</Dropzone>
<button onClick={ this.onCancelUpload }>Cancel</button>
Please advise, if It is possible using react-dropzone. I can't think of a way to stop the event that is already triggered and is uploading the file.
I was able to solve the above problem using the Axios Cancel Token.
Use Axios to handle the upload as a promise.
Create a source at the start of your code.
const CancelToken = axios.CancelToken;
let source = CancelToken.source();
Pass on the source to the request in config.
const {
acceptedFiles
} = this.state;
const uploaders = acceptedFiles.map((file) => {
const formData = new FormData();
// data must be set BEFORE sending file
formData.append('document', file);
const uploadConfig = {
onUploadProgress: (progressEvent) => {
const progressUpload = (progressEvent.loaded * 100) / progressEvent.total;
this.setState({
progressUpload
});
},
cancelToken: source.token,
};
return inkerzApi.post('/uploads/file', formData, uploadConfig)
.then(response => response.data)
.catch(() => console.log('Upload canceled'));
});
Promise.all(uploaders).then((filesMetadata) => {
filesMetadata.forEach((metadata) => {
if (metadata && metadata.mediaLink && metadata.totalPages) {
this.onNewFileUploaded(metadata);
// show success message here
} else if (this.state.uploadCanceled) {
// show cancelation notification here
this.setState({
uploadCanceled: false
});
}
});
this.setState({
acceptedFiles: [],
progressUpload: 0,
});
});
On Cancel Button Click
onCancelUpload = () => {
source.cancel('Operation canceled by the user.');
source = CancelToken.source();
this.setState({ uploadCanceled: true });
}
This worked out for me. Hope this helps others as well.
Related
Has anyone uploaded an image using Node, React, Antd library to cloudinary? I'm getting an error saying that the file is missing. I'm not sure if I should be sending the entire file object or just the name. I have sent the thumburl (sometimes it works others it doesn't there has to be something I'm doing wrong).
This is my backend
const uploadImage = async (req, res) => {
try {
const result = await cloudinary.uploader.upload(req.body.image, {
public_id: `${Date.now()}`,
resource_type: 'auto' //jpeg or png
});
res.json({
public_id: result.public_id,
url: result.secure_url
});
} catch (error) {
console.log(error);
res.status(400).send(error)
}}
This is my frontend:
const Uploader = () => {
const { token } = useSelector(state => state.user);
const handleChange = ({file}) => {
console.log(file)
axios.post(`${process.env.REACT_APP_API}/uploadImages`, {file}, {
headers:{
authtoken: token
}
})
.then(res => console.log(res))
.catch(err => console.log(err))
};
return (
<Upload
listType="picture"
showUploadList
onChange={handleChange}
>
<Button>Upload</Button>
</Upload>
)
}
export default Uploader
IDK why the onChange triggers 3 times when it has worked I've sent the thumbUrl and it uploads 3 times, I have seen that I can use beforeUpload but I believe this works before uploading. I want to upload, preview the image and sending it to the server and then send it to my Form Component to add it to the values I have there
Anyone who has already uploaded could help or any ideas would be appreciated?
When it comes to the file not uploading properly, I am guessing it is because the res.body.image isn't the actual file. I would look at what the value is there. I would guess you are missing a middleware.
As far as your frontend issue, I'm still a little unclear about what the issue you are having exactly is. For an example of a clean frontend upload I would check out https://codesandbox.io/embed/jq4wl1xjv. You could also consider using the upload widget which will handle the preview as well as give you some easy editing options.
https://cloudinary.com/documentation/upload_widget
I was able to figure it out, indeed there is a need of a middleware, I used formidable on the routes of my backend
router.post('/uploadImages', authCheck, adminCheck, formidable({maxFileSize: 5 * 1024 * 1024}), imageUpload);
And fixed the controller
const imageUpload = async (req, res) =>{
try {
//console.log('req files:', req.files.file.path)
const result = await cloudinary.uploader.upload(req.files.file.path, {
public_id: `${Date.now()}`,
resource_type: 'auto'
});
res.json({
public_id: result.public_id,
url: result.secure_url
})
} catch (error) {
console.log(error)
}
}
As far as the Frontend goes. Ant designs component takes an action that makes the POST to the backend, and it also takes headers if needed (in my case it takes an authtoken for firebase).
When the image is uploaded it makes the POST to the backend based on the "action". The response will bring back the "FileList" with the upload URL to access it.
<Upload
listType="picture"
showUploadList
multiple
action={`${process.env.REACT_APP_API}/uploadImages`}
headers={{authtoken: token}}
>
<Button>Upload Images</Button>
</Upload>
I hope this helps somebody else too
I tried this and it's worked for me.
Here I used Multer middleware for handling form data(for uploading files).
I used this route,
router.post('/uploadImages', authMiddleware, multer.single("image"), imageUpload);
Corrected controller,
try {
let result;
if (req.file) {
result = await cloudinary.uploader.upload(req.files.file.path, {
public_id: `${Date.now()}`,
resource_type: 'auto'
});
}
res.json({
public_id: result.public_id,
url: result.secure_url
})
} catch (error) {
console.log(error)
}
}
//Multer config file,
const multer = require("multer");
const path = require("path");
module.exports = multer({
storage: multer.diskStorage({}),
fileFilter: (req, file, cb) => {
let ext = path.extname(file.originalname);
if (
ext !== ".jpg" &&
ext !== ".jpeg" &&
ext !== ".png" &&
ext !== ".PNG" &&
ext !== ".JPG" &&
ext !== ".JPEG"
) {
cb(new Error("File type is not supported"), false);
return;
}
cb(null, true);
},
});
I am trying to add a file (jpeg file) into the firebase storage in a Cypress e2e test. It runs fine and the test case passes locally. I login the user, perform all the required actions (clicking button, attach file from fixtures folder to the input tag for file, save changes by clicking button).
But issue occurs in Github actions CI/CD pipeline, All the testcases pass but error occurs on this test case that Cannot read properties of undefined (Reading name). I am accessing fileselected.name when changes are saved.
My assumption is that the file is not found by cypress or there is an issue related to permissions in firebase storage that is why it is failing. Can anybody help/guide me about why is it not working?
My Cypress test:
it("adds company successfully", () => {
cy.visit("http://127.0.0.1:3000/dashboard/company", {
headers: { "Accept-Encoding": "gzip, deflate" },
}).then(() => {
cy.wait(10000);
cy.get("#newCompany").click();
cy.get("#companyName").type("Test company").blur();
cy.get("#companyEmail").type("testCompany#gmail.com").blur();
cy.get("input[type=file]").selectFile(
{
contents: "cypress/fixtures/file.jpeg",
fileName: `file${new Date().toISOString()}.jpeg`,
},
{
force: true,
}
);
cy.get(".purple-button").contains("Save Changes").click();
});
});
The handleSave() function runs on clicking Save Changes button.
const handleSave = () => {
setIsSubmitted(true);
if (isFormValid()) {
if (fileSelected.name) {
handleUpload();
} else {
saveCompany(companyPictureUrl);
props.handleClose();
}
}
};
const handleUpload = () => {
try {
setIsUploading(true);
const storageRef = ref(storage, "company/" + fileSelected.name);
const uploadTask = uploadBytesResumable(storageRef, fileSelected);
uploadTask.on(
"state_changed",
(snapshot: { bytesTransferred: number; totalBytes: number }) => {
const progress =
Math.round(
(snapshot.bytesTransferred / snapshot.totalBytes) * 1000
) / 10;
setUploadProgress(progress);
},
(error: any) => {
console.log(error);
},
async () => {
try {
const url = await getDownloadURL(storageRef);
setCompanyPictureUrl(url);
saveCompany(url);
props.handleClose();
} catch (error: any) {
console.log(error);
}
}
);
} catch (ex) {
console.log("Error while uploading file :: ", ex);
}
};`
Github actions error:
Basically I want to record audio and I'm using react-mic for this but it gives back blob but I want mp3 or wav file to send to the backend. How to POST blob file to the server?
This is my code in App.js
import React from "react";
import { ReactMic } from "react-mic";
import $ from "jquery";
import { findDOMNode } from "react-dom";
import "./App.css";
class App extends React.Component {
handleDisplayForStart = () => {
const startBtn = findDOMNode(this.refs.startBtn);
$(startBtn).addClass("d-none");
const stopBtn = findDOMNode(this.refs.stopBtn);
$(stopBtn).removeClass("d-none");
};
handleDisplayForStop = () => {
const stopBtn = findDOMNode(this.refs.stopBtn);
$(stopBtn).addClass("d-none");
const processBtn = findDOMNode(this.refs.processBtn);
$(processBtn).removeClass("d-none");
};
constructor(props) {
super(props);
this.state = {
blobURL: null,
recordedBlob: null,
record: false,
};
}
startRecording = () => {
this.setState({ record: true });
this.handleDisplayForStart();
};
stopRecording = () => {
this.setState({ record: false });
this.handleDisplayForStop();
};
onData(recordedBlob) {
console.log("chunk of real-time data is: ", recordedBlob);
}
onStop = (recordedBlob) => {
console.log(recordedBlob.blobURL);
const blobURL = recordedBlob.blobURL;
this.setState({ blobURL: blobURL });
this.onUpload();
return recordedBlob.blobURL;
};
onUpload = (recordedBlob) => {
// var form = new FormData();
// form.append("file", recordedBlob);
// fetch("http://localhost:3000/audio", {
// // content-type header should not be specified!
// method: "POST",
// body: form,
// })
// .then(function (response) {
// return response.text();
// })
// .then(function (text) {
// console.log(text); // The text the endpoint returns
// })
// .catch((error) => console.log(error));
};
render() {
return (
<div className="App">
<ReactMic
visualSetting="frequencyBars"
// mimeType='audio/mp3'
record={this.state.record}
className="d-none"
onStop={this.onStop}
onData={this.onData}
/>
<button
ref="startBtn"
className="start-btn"
onClick={this.startRecording}
type="button"
>
START
</button>
<button
ref="stopBtn"
className="stop-btn concentric-circles d-none"
onClick={this.stopRecording}
type="button"
>
STOP
</button>
<button
ref="processBtn"
className="process-btn d-none"
onClick={this.onUpload}
>
Processing..
</button>
<br />
<audio src={this.state.blobURL} controls />
</div>
);
}
}
export default App;
I tried various things from medium blogs and different StackOverflow answers but anything doesn't work correctly.
This may be a bit late, i also came across this problem and i also couldn't find a solution, sharing the solution so it may help others.
code for sending recorded blob file using react-mic to server,
const onStop = (blob) => {
const formData = new FormData();
// if you print blob in console you may get details of this obj
let blobWithProp = new Blob([blob["blob"]], blob["options"]);
formData.append("file", blobWithProp);
const postRequest = {
method: "POST",
body: formData,
};
fetch("http://127.0.0.1:5000/audio_out/", postRequest)
.then(async (res) => {
const data = await res.json();
console.log(data);
if (!res.ok) {
const err = (data && data.message) || res.status;
return Promise.reject(err);
}
})
.catch((err) => {
console.log(err);
});
};
Here onStop() is the callback function which is executed when recording stopped.
On your commented part of your code you are sending recorded blob file directly. I don't know much about it, but if you log the received file on console you may see it is an object file containing blob, options, etc. So, I think we need to create a new Blob file from that. I am just a beginner in web development, may be i am wrong. But the above code solved the problem.
I am working on a project that need to store images on Firebase storage. I use Ckeditor to upload image, I follow Ckeditor's docs to write my own image upload adapter, but it din't not work. I cannot upload image to firebase and get back the image url. here is what I've tried
class MyUploadAdapter {
constructor(loader) {
this.loader = loader;
}
upload() {
return this.loader.file
.then(file => new Promise((resolve, reject) => {
this._initRequest();
this._initListeners(resolve, reject, file);
this._sendRequest(file);
}));
}
abort() {
if (this.xhr) {
this.xhr.abort();
}
}
_initRequest() {
const xhr = this.xhr = new XMLHttpRequest();
xhr.open('POST', 'https://firebasestorage.googleapis.com/v0/b/smart-farming-e3e2d.appspot.com/o/images/', true);
xhr.responseType = 'json';
}
// Initializes XMLHttpRequest listeners.
_initListeners(resolve, reject, file) {
const xhr = this.xhr;
const loader = this.loader;
const genericErrorText = `Couldn't upload file: ${file.name}.`;
xhr.addEventListener('error', () => reject(genericErrorText));
xhr.addEventListener('abort', () => reject());
xhr.addEventListener('load', () => {
const response = xhr.response;
if (!response || response.error) {
return reject(response && response.error ? response.error.message : genericErrorText);
}
resolve({
default: response.url
});
});
if (xhr.upload) {
xhr.upload.addEventListener('progress', evt => {
if (evt.lengthComputable) {
loader.uploadTotal = evt.total;
loader.uploaded = evt.loaded;
}
});
}
}
_sendRequest(file) {
const data = new FormData();
data.append('upload', file);
this.xhr.send(data);
}
}
function MyCustomUploadAdapterPlugin(editor) {
editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
return new MyUploadAdapter(loader);
};
}
const editorConfiguration = {
extraPlugins: [MyCustomUploadAdapterPlugin],
};
and I call it to use in my Component like this
<CKEditor
editor={ClassicEditor}
// data="<p>Hello from CKEditor 5!</p>"
onInit={editor => {
// console.log('Editor is ready to use!', editor);
}}
onChange={(event, editor) => {
const data = editor.getData();
console.log({ event, editor, data });
}}
config={editorConfiguration}
/>
Can anyone have experience with upload image from Ckeditor to firebase storage help me? Thanks u.
I have also been trying to fix this for ages now. I have now been able to upload an image to my server by adding CORS to the FireBase URL
xhr.open('POST', `https://cors-anywhere.herokuapp.com/https://firebasestorage.googleapis.com/v0/b/smart-farming-e3e2d.appspot.com/o/${file.name}`, true);
If you are wondering how I got the file attribute you need to pass it in the function
_initRequest(file) {
console.log(file.name)
const xhr = this.xhr = new XMLHttpRequest();
xhr.open('POST', `https://cors-anywhere.herokuapp.com/FIREBASE_URL_HERE{file.name}`, true);
xhr.responseType = 'json';
}
This should now upload your image to your Firebase storage
i make a request to server to load data related to item on clicking item button. However, on clicking cancel button it should abort the request to load data. How can i do it. Below is the code,
componentDidMount() {
if(this.props.item) {
this.load_item_data();
}
}
load_item_data = () => {
const props = this.props;
this.file_download_status = {};
if (this.on_item_changed) {
this.on_item_changed();
}
const item_changed = new Promise((resolve) => { this.on_item_changed =
resolve; });
const abort_loading = Promise.race([item_changed, this.unmount]);
item
.load(props.item.id, gl, this.update_download_progress,
abort_loading).then((result) => {
this.files = result.files;
this.setState({
item_download_done: true,
});
client.add_item_view(props.item.id, abort_loading);
});
How can i abort the above request on clicking a cancel button? Thanks.
Here's how it's in the documentation of Axios
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
}).catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// handle error
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.');
Check the link:-
https://github.com/axios/axios#cancellation