File upload progress using react-dropzone - reactjs

Using react-dropzone to upload the file, I want to achieve the file progress like in percentage of file transfer or mbs data transfer.
Here is the link of: https://react-dropzone.netlify.com/
onDrop(acceptedFiles, uploadApi) {
const filesToBeSent = this.state.filesToBeSent;
if (acceptedFiles.length) {
if (acceptedFiles[0].type === FileTypeList.TYPE) {
filesToBeSent.push(acceptedFiles);
const formData = new FormData();
formData.append("file", acceptedFiles[0]);
uploadApi(formData).then((response) => {
this.setState({
filesPreview: [],
filesToBeSent: [{}],
showNotification: true,
uploadResponse: response,
});
this.props.fetchHistory();
});
} else {
this.setState({
fileType: true,
});
}
} else {
this.setState({
fileSize: true,
});
}
}
<Dropzone maxSize={this.props.maxSize} onDrop={(files) => this.onDrop(files, this.props.uploadApi)}>
{({ getRootProps, getInputProps }) => {
return (
<div {...getRootProps()} className={"dropzone"}>
<UploadPanel id="uploadFileContainerId">
<p>
<img id="uploadImage" src={UploadImage} />
</p>
<input {...getInputProps()} />
<div>{t("assets:UPLOAD_FILE")}</div>
<Note>
{this.props.maxSizeTitle ? t("workers:UPLOAD_WORKER_FILE_SIZE") : t("assets:UPLOAD_FILE_SIZE")}
</Note>
</UploadPanel>
</div>
);
}}
</Dropzone>

In case you wanna detect file upload process you can use XMLHttpRequest
onDrop(acceptedFiles) {
const formData = new FormData();
formData.append('file', acceptedFiles[0])
const xhr = new XMLHttpRequest();
xhr.open(/*params*/);
xhr.send(formData)
xhr.upload.onprogress = event => {
const percentages = +((event.loaded / event.total) * 100).toFixed(2);
this.setState({percentages})
};
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) return;
if (xhr.status !== 200) {
/*handle error*/
}
/*handle success*/
};
}

You can use React Dropzone Uploader, which gives you file previews with upload progress out of the box, and also handles uploads for you.
import 'react-dropzone-uploader/dist/styles.css'
import Dropzone from 'react-dropzone-uploader'
const Uploader = () => {
return (
<Dropzone
getUploadParams={() => ({ url: 'https://httpbin.org/post' })} // specify upload params and url for your files
onChangeStatus={({ meta, file }, status) => { console.log(status, meta, file) }}
onSubmit={(files) => { console.log(files.map(f => f.meta)) }}
accept="image/*,audio/*,video/*"
/>
)
}
Uploads can be cancelled or restarted. The UI is fully customizable.
Full disclosure: I wrote this library to address some of the shortcomings and excessive boilerplate required by React Dropzone.

Here is another example based on turchak's answer for handling any number of files:
onDrop(acceptedFiles) {
const formData = new FormData();
for (const file of acceptedFiles) formData.append('file', file);
const xhr = new XMLHttpRequest();
xhr.upload.onprogress = event => {
const percentage = parseInt((event.loaded / event.total) * 100);
console.log(percentage); // Update progress here
};
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) return;
if (xhr.status !== 200) {
console.log('error'); // Handle error here
}
console.log('success'); // Handle success here
};
xhr.open('POST', 'https://httpbin.org/post', true);
xhr.send(formData);
}

Related

How can I check if the data_URL is returning an image of video ? - Firebase & Next.js/React

The image is uploaded to firebase and returned as a data_URL that looks like this:
https://firebasestorage.googleapis.com/v0/b/app_name/o/posts%2postId?alt=media&token=token
I am trying to check if the file type is a video or an image, then return a div depending on the "mediaType". Because firebase storage doesn't include the file extension in the url, it is difficult to determine the file type.
First attempt:
const [mediaType, setMediaType] = useState(null);
useEffect(() => {
if (postImage) {
const storageRef = firebase.storage().ref();
storageRef.child(postImage).getDownloadURL().then(url => {
fetch(url)
.then(res => res.blob())
.then(blob => {
let type = blob.type;
if (type.startsWith("image")) {
setMediaType("image");
} else if (type.startsWith("video")) {
setMediaType("video");
} else {
setMediaType("other");
console.log("Unknown file format: " + type);
}
});
});
}
}, [postImage]);
Second attempt:
const handleFile = async (e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = async (e) => {
const dataURL = e.target.result;
if (dataURL.startsWith('data:image/')) {
setMediaType('image');
setDataUrl(dataURL);
console.log("Image: " + dataURL);
} else if (dataURL.startsWith('data:video/')) {
setMediaType('video');
setDataUrl(dataURL);
console.log("Video: " + dataURL);
} else {
let response = await fetch(dataURL);
let type = response.headers.get("Content-Type");
if (type.startsWith("image")) {
setMediaType("image");
setDataUrl(dataURL);
} else if (type.startsWith("video")) {
setMediaType("video");
setDataUrl(dataURL);
} else {
setMediaType("other");
console.log("Unknown file format: " + type);
}
}
}
reader.readAsDataURL(file);
}
The div inside my return statement:
<div className="w-full px-3">
{mediaType === 'image' ? <img className="shadow-md w-full" src={postImage || 'default-image.jpg'} alt="" /> : null}
{mediaType === 'video' ? <ReactPlayer layout="fill" url={postImage} config={{file:{attributes:{controlsList:'nodownload'}}}} controls onContextMenu={e => e.preventDefault()}/> : null}
{mediaType === 'other' ? <p>File is not an image or video</p> : null}
</div>
What I would do is add metadata while uploading the file to firebase. You can check the documentation to see how:
//This is a metadata, you can customize these
//as you can see the content type is set to be image/jpeg
var newMetadata = {
cacheControl: 'public,max-age=300',
contentType: 'image/jpeg'
.........
.........
};
You will use this metadata while uploading the file using:
storageRef.updateMetadata(newMetadata).......
And when reading the file read out the metadata that you set to the file to for example detect its type:
storageRef.getMetadata().then((metadata) => {
//use this metadata to know the type here.......
})
Hope this gives you an idea of what to do.

PUT Request - Uploading a file - React / Express / Multer - req.file = undefined

I'm developping a social network for a school project, and I want to allow the user to update his/her information, specifically the profile Photo and the cover Photo.
I use multer for storing images.
When I try to upload an image using a POST request, it works perfectly fine but on a PUT request it says req.file /req.files is always undefined.
// FORM (IMPORTANT PART)
<form
className="update__form"
onSubmit={handleSubmit}
encType="multipart/form-data"
id="form"
>
{/* GESTION PHOTO DE COUVERTURE */}
<div className="update__form-cover">
<input
type="file"
name="coverPhotoUrl"
className="update__form-cover-input"
id="cover"
accept="image/*"
onChange={handleCover}
/>
<div className="update__form-cover-button">
Modifier la photo de couverture
</div>
</div>
<div
className={
loadCover === true
? 'update__form-cover-img'
: 'update__form-cover-img--close'
}
>
<img id="coverImg" alt="ok" />
</div>
{/* GESTION PHOTO DE PROFIL */}
<div className="update__form-profile">
<input
type="file"
name="profilePhotoUrl"
className="update__form-profile-input"
id="profile"
accept="image/*"
onChange={handleProfile}
/>
<div className="update__form-profile-button">
Modifier la photo de profil
</div>
</div>
<div
// MY DIFFERENTS FUNCTIONS
// TO DISPLAY AND STORE THE NEW COVER (USESTATE)
const handleCover = () => {
const coverChange = document.getElementById('cover').files
if (coverChange.length > 0) {
const fileReader = new FileReader()
fileReader.onload = function (event) {
document
.getElementById('coverImg')
.setAttribute('src', event.target.result)
setLoadCover(true)
setData({
...data,
coverPhotoUrl: coverChange[0],
})
}
fileReader.readAsDataURL(coverChange[0])
}
}
// DISPLAY AND STORE THE NEW PROFILE PHOTO (USESTATE)
const handleProfile = () => {
const profileChange = document.getElementById('profile').files
setData({
...data,
profilePhotoUrl: profileChange[0].name,
})
if (profileChange.length > 0) {
const test = new FileReader()
test.onload = function (event) {
document
.getElementById('profileImg')
.setAttribute('src', event.target.result)
setLoadProfile(true)
}
test.readAsDataURL(profileChange[0])
}
}
// FUNCTION CALLED WHEN FORM IS SUBMITTED
const handleSubmit = (event) => {
event.preventDefault()
try {
updateUser(data)
} catch (err) {
console.log(err)
}
}
// FUNCTION TO FETCH PUT
const updateUser = (data) => {
console.log(data)
const userId = localStorage.getItem('userId')
fetch('http://localhost:8000/api/user/' + userId, {
method: 'PUT',
headers: {
'Content-Type': 'form-data',
},
body: JSON.stringify(data),
})
}
export default updateUser
// BACK CONFIG
const multer = require('multer');
const MIME_TYPES = {
'image/jpg': 'jpg',
'image/jpeg': 'jpeg',
'image/png': 'png',
'image/svg': 'svg',
}
const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, '../images')
},
filename: (req, file, callback) => {
const name = file.originalname.split(' ').join('_');
const extension = MIME_TYPES[file.mimetype];
callback(null, name + Date.now() + '.' + extension);
}
});
const upload = multer({ storage: storage });
router.put('/:id', upload.array(), userCtrl.updateUser);
// CONTROLLER (not very important HERE BUT RETURN REQ.FILE UNDEFINED)
exports.updateUser = ((req, res, next) => {
console.log(req.file)
console.log(req.files)
const userInfos = req.file ? {
...JSON.parse(req.body.data),
coverPhotoUrl: `${req.protocol}://${req.get('host')}/images/${req.file.filename}`
} : {
...req.body
};
delete userInfos._userId;
User.findOne({
_id: req.params.id
})
.then((user)=> {
User.updateOne({
_id: req.params.id
},
{
...userInfos,
_id: req.params.id
})
.then(()=> res.status(200).json({ message : 'infos mises à jour ! '}))
.catch((error)=> res.status((401)).json({ error }));
})
.catch((error)=> res.status(400).json({ error }));
});
If someone come on this, i've finally found the answer :
first : use the formData constrcutor form-data-infos mdn, to put your datas in (formdata.append())
second : on the function who will fetch (front to back) : just remove the 'content-type', the browser will automatically set this,
it should run normally then
I also want to apologize for this 'useless' post because all the answers were on this forum (and on the web), my eyes were just closed

Getting error as 'user cannot publish stream' while connecting to live stream

I am using agora SDK for connecting live with one user. I know when that user is live but, unable to connect. Following is the code reference:
function App() {
const [joined, setJoined] = useState(false);
const channelRef = useRef("");
const remoteRef = useRef("");
const leaveRef = useRef("");
async function handleSubmit(e) {
try {
if (channelRef.current.value === "") {
return console.log("Please Enter Channel Name");
}
setJoined(true);
rtc.client = AgoraRTC.createClient({ mode: "live", codec: "vp8" })
await rtc.client.join(
options.appId,
options.channel,
options.token || null,
options.uid || null,
);
// Create an audio track from the audio captured by a microphone
rtc.localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack();
// Create a video track from the video captured by a camera
rtc.localVideoTrack = await AgoraRTC.createCameraVideoTrack();
rtc.localVideoTrack.play("local-stream");
rtc.client.on("user-published", async (user, mediaType) => {
// Subscribe to a remote user
await rtc.client.subscribe(user, mediaType);
console.log("subscribe success");
// console.log(user);
if (mediaType === "video" || mediaType === "all") {
// Get `RemoteVideoTrack` in the `user` object.
const remoteVideoTrack = user.videoTrack;
console.log(remoteVideoTrack);
// Dynamically create a container in the form of a DIV element for playing the remote video track.
const PlayerContainer = React.createElement("div", {
id: user.uid,
className: "stream",
});
ReactDOM.render(
PlayerContainer,
document.getElementById("remote-stream")
);
user.videoTrack.play(`${user.uid}`);
}
if (mediaType === "audio" || mediaType === "all") {
// Get `RemoteAudioTrack` in the `user` object.
const remoteAudioTrack = user.audioTrack;
// Play the audio track. Do not need to pass any DOM element
remoteAudioTrack.play();
}
});
rtc.client.on("user-unpublished", (user) => {
// Get the dynamically created DIV container
const playerContainer = document.getElementById(user.uid);
console.log(playerContainer);
});
// Publish the local audio and video tracks to the channel
await rtc.client.publish([rtc.localAudioTrack, rtc.localVideoTrack]);
console.log("publish success!");
} catch (error) {
console.error(error);
}
}
async function handleLeave() {
try {
const localContainer = document.getElementById("local-stream");
rtc.localAudioTrack.close();
rtc.localVideoTrack.close();
setJoined(false);
localContainer.textContent = "";
// Traverse all remote users
rtc.client.remoteUsers.forEach((user) => {
// Destroy the dynamically created DIV container
const playerContainer = document.getElementById(user.uid);
playerContainer && playerContainer.remove();
});
// Leave the channel
await rtc.client.leave();
} catch (err) {
console.error(err);
}
}
return (
<>
<div className="container">
<input
type="submit"
value="Join"
onClick={handleSubmit}
disabled={joined ? true : false}
/>
<input
type="button"
ref={leaveRef}
value="Leave"
onClick={handleLeave}
disabled={joined ? false : true}
/>
</div>
{joined ? (
<>
<div id="local-stream" className="stream local-stream"></div>
<div
id="remote-stream"
ref={remoteRef}
className="stream remote-stream"
></div>
</>
) : null}
</>
);
}
export default App;
I can connect but, it shows video from my side, which is not expected. I should see video of other side live. So what code changes needed to be done to enable live session?
Following is the error image
while live streaming there are different client roles that need to be assigned.
see this for reference
You need to initialize it to "host" by
rtc.client = AgoraRTC.createClient({ mode: "live", codec: "vp8" })
rtc.client.setClientRole("host")
await rtc.client.join(
options.appId,
options.channel,
options.token || null,
options.uid || null,
);
HTH!

How to use CustomRequest with antd upload function to upload images to firebase?

I'm trying to use the antd upload component and pictures wall example to upload images to my firebase storage.
Initially i tried using the action property as out lined here with the same results. I then tried using the customRequest form as outlined in the solution to that question. After struggling all day, I just can't seem to get it to work. Clearly I don't understand whats going on well enough.
My various change functions..
handleCancel = () => this.setState({ previewVisible: false });
handlePreview = async file => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj);
}
this.setState({
previewImage: file.url || file.preview,
previewVisible: true,
});
};
handleChange = (info) => {
console.log('handleChange',info);
if (info.file.status === 'uploading') {
console.log('setting loading to true');
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
console.log('setting loading to false');
getBase64(info.file.originFileObj, imageUrl => this.setState({
imageUrl,
loading: false
}));
}
};
my customupload function..
customUpload = async ({ onError, onSuccess, file }) => {
console.log("customUpload called");
console.log(file);
const storage = firebase.storage();
const metadata = {
contentType: 'image/jpeg'
};
const storageRef = await storage.ref();
// const imageName = generateHashName() //a unique name for the image
const imgFile = storageRef.child(`Property Photos/${file}.png`);
try {
const image = await imgFile.put(file, metadata);
onSuccess(null, image);
}
catch(e) {
onError(e);
};
};
my render JSX..
<Form.Item label="Photos">
<div className="clearfix">
<Upload
listType="picture-card"
fileList={fileList}
multiple={true}
accept="image"
onPreview={this.handlePreview}
onChange={this.handleChange}
customRequest={this.customUpload}
>
{this.imageUrl ? <img src={this.imageUrl} alt="avatar" /> : uploadButton}
</Upload>
<Modal visible={previewVisible} footer={null} onCancel={this.handleCancel}>
<img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal>
</div>
</Form.Item>
Funny enough it runs and sort of works, but seems to have three issues..
When I upload an image(s) the card/box(s) shows "Upload" with the animated spinning wheel forever. It never completes and shows the image thumbnail.
When selecting mulitple images, only the first one seems to end up on firebase. Never more..
When selecting the image(s) to upload and clicking OK, there is a long pause (5 seconds?) like the application hangs before i can click anything again. not sure why that is.
It feels like i just don't understand how to use this customRequest property..
In general, what has to be done is: connect the "onProgress", "onSuccess" and "onError" callback functions of 'antd Upload' to the corresponding event observers of cloudStorage upload function.
https://firebase.google.com/docs/storage/web/upload-files#monitor_upload_progress
So, the customRequest function can be as:
let customRequest = ({
file,
onSuccess,
onError,
onProgress,
}) => {
var uploadTask = storageRef.child(`images/${file.name}`).put(file);
// Register three observers:
// 1. 'state_changed' observer, called any time the state changes
// 2. Error observer, called on failure
// 3. Completion observer, called on successful completion
uploadTask.on(
"state_changed",
function (snapshot) {
// Observe state change events such as progress, pause, and resume
// Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
var progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Upload is " + progress + "% done");
// CONNECT ON PROGRESS
onProgress(progress)
switch (snapshot.state) {
case firebase.storage.TaskState.PAUSED: // or 'paused'
console.log("Upload is paused");
break;
case firebase.storage.TaskState.RUNNING: // or 'running'
console.log("Upload is running");
break;
}
},
function (error) {
// Handle unsuccessful uploads
// CONNECT ON ERROR
onError(error)
},
function () {
// Handle successful uploads on complete
// For instance, get the download URL: https://firebasestorage.googleapis.com/...
uploadTask.snapshot.ref
.getDownloadURL()
.then(function (downloadURL) {
console.log("File available at", downloadURL);
// CONNECT ON SUCCESS
onSuccess(downloadURL) // Pass any parameter you would like
});
}
);
};

Post Excel file/Blob to Rest API using Angular 4

I am using input type with file to upload a excel file like below :
<input type="file" style="display: inline-block;" (change)="readFile($event)" placeholder="Upload file" accept=".xlsx">
<button type="button" class="btn btn-info" (click)="uploadExcel()" [disabled]="!enableUpload">Upload</button>
For reading file contents :
public readFile(event) {
try {
this.enableUpload = false;
const reader = new FileReader();
reader.onload = (e: any) => {
console.log(e.target.result);
this.fileContent = e.target.result;
let binary = "";
const bytes = new Uint8Array(e.target.result);
const length = bytes.byteLength;
for (let i = 0; i < length; i++) {
binary += String.fromCharCode(bytes[i]);
}
const workbook = XLSX.read(binary, { type: 'binary' });
console.log(workbook);
};
reader.readAsArrayBuffer(file);
console.log(reader);
this.enableUpload = true;
} catch (error) {
console.log('Error reading uploaded file. See below msg for details : \n');
console.log(error);
}
}
on click of Upload below code is used to upload the file content.
public uploadExcel() {
let formData = new FormData();
formData.append('file', this.fileContent, 'filename');
this._commonService
.commonHttpPostRequest("http://urlforfileupload",
{ file: this.fileContent }, {}
)
.subscribe(response => {
try {
console.log(response);
} catch (error) {
console.log("Error" + error);
}
});
}
But I am unable to upload file contents and getting the below response :
400 Bad Request
{
"status": "bad_input",
"message": "file not found in request payload."
}
I can see fileContent before I post the request.
Finally found the solution
Template :
<input type="file" [multiple]="multiple" #fileInput class="browse-btn" (change)="readFile()" accept=".xlsx">
<button type="button" class="btn btn-info btn-lg" (click)="upload()" >Upload</button>
Component :
public upload() {
const inputEl: HTMLInputElement = this.inputEl.nativeElement;
const fileCount: number = inputEl.files.length;
const formData = new FormData();
const headers = new Headers();
headers.set('Accept', 'application/json');
headers.delete('Content-Type'); // mandate for accepting binary content
if (fileCount > 0) {
for (let i = 0; i < fileCount; i++) {
formData.append('file', inputEl.files.item(i));
}
try {
this.loaderForFileUpload = true;
this.http
.post('http://urlForFileUpload', formData, { headers: headers })
.subscribe(response => {
if (response.status === 200) {
this._toastr.success('File uploaded successfully', 'Success!');
}
}, error => {
this._toastr.error('File contents mismatch', error.statusText);
});
} catch (e) {
console.log('Error occured while posting uploaded file. See below message for details : \n');
console.log(e);
}
}
}

Resources