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
Related
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.
I created a simple dapp with react/truffle/web3 that allows users to enter a number of tokens and submit it to stake it, the problem that I got is that when clicked on submit button, Metamask popup to confirm the transaction(approuve) but I don't get the second popup to confirm stakeTokens function, hence, no transaction history displayed.
Dapp link: https://doxa-staking.netlify.app.
The stake smart contract code deployed on rinkeby: https://rinkeby.etherscan.io/address/0xAD015a006755b389d8e5BC2680cc5081dc1d0abd#code
The reactjs web3 code in github:
https://github.com/isofttechn/staking-dapp/blob/main/src/components/Home/index.js
My Stake Approval Function
approval = async () => {
const { web3, token, address } = this.props.wallet;
const tokenAmount = web3.utils.toWei("99999999", "ether");
const stakingContractAddress = process.env.REACT_APP_DOXACONTRACT_ADDRESS;
const approval = await token.methods
.approve(stakingContractAddress, tokenAmount)
.send({ from: address });
console.log(approval);
this.setState({ isApproved: true });
await this.checkAllowance();
};
My Stake Function:
stakeAZO = async () => {
this.setState({ stakeloading: true });
let type;
if (this.state.stakeType) {
type = 1;
} else type = 0;
if (this.state.stakeValue > 0 && this.state.yearsValue > 0) {
const { web3, staking, token, address } = this.props.wallet;
const tokenAmount = web3.utils.toWei(
this.state.stakeValue.toString(),
"ether"
);
var time = Date.now();
console.log("address", [
address,
process.env.REACT_APP_DOXACONTRACT_ADDRESS,
tokenAmount,
type,
this.state.yearsValue,
time,
]);
const requestOptions = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
userAddress: address,
contractAddress: process.env.REACT_APP_DOXACONTRACT_ADDRESS,
amount: tokenAmount,
id: type,
noOfDays: this.state.yearsValue,
timestamp: time,
}),
};
const response1 = await fetch(
process.env.REACT_APP_API_URL + "/getsignature",
requestOptions
);
const data1 = await response1.json();
var signaturelkn = data1.result;
var sigtuplelkn = [
address,
process.env.REACT_APP_DOXACONTRACT_ADDRESS,
tokenAmount,
type,
this.state.yearsValue,
time,
signaturelkn,
];
try {
const stake = await staking.methods
.stake(tokenAmount, this.state.yearsValue, sigtuplelkn)
.send({ from: address });
this.setState({ stakeloading: false });
} catch (err) {
console.log(err);
this.setState({ stakeloading: false });
}
// console.log(stake);
this.getStakeRecords();
if (this.state.stakeType) {
this.getTokenBalance();
this.setState({ stakeloading: false });
}
} else {
this.setState({ stakeloading: false });
alert("Amount of AZO or days should be more than 0!");
}
};
How I rendered it:
<div className="d-block d-md-flex bottom-btn-cont">
{!this.state.isApproved && (
<button
className="btn-btn stake-btn"
onClick={() =>
this.props.wallet.connected
? this.approval()
: alert("Connect to wallet!")
}
>
Approve AZO
</button>
)}
<button
className="btn-btn stake-btn"
style={{
backgroundColor: this.state.isApproved
? "#002365"
: "#e2e2e2",
}}
disabled={!this.state.isApproved}
onClick={() =>
this.state.isApproved
? this.stakeAZO()
: alert("Approve tokens before staking!")
}
>
STAKE AZO{" "}
{this.state.stakeloading ? (
<img
style={{ width: "20px", height: "20px" }}
src={buyLoader}
></img>
) : null}
</button>
</div>
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!
My project has React as frontend and Laravel as backend.
I am trying to upload a file and the details of the file appear in state while uploading but not in the controller and hence not able to upload it in folder.
Component has
<div className="form-group col-sm-4">
<div className="imgPreview">{$imagePreview}</div>
<label>Profile Pic</label>
<input className="form-control" type="file" name="profile_pic" onChange={(e)=>this._handleImageChange(e)}/>
</div>
further codes are:
_handleImageChange(e) {
e.preventDefault();
let reader = new FileReader();
let file = e.target.files[0];
reader.onloadend = () => {
this.setState({
fileselected: file,
profile_pic: file.name,
imagePreviewUrl: reader.result
});
}
reader.readAsDataURL(file)
}
Data gets uploaded though axios
submitHandler = e =>{
e.preventDefault()
this.props.drprofilecreate(this.state)
}
export const drprofilecreate = (data) => dispatch =>{
console.log("Coming form profile create action ")
console.log(data)
return axios.post('/api/v1/drProfileCreate', data)
.then( res =>res.data )
.then(drprofile =>
dispatch({
type: DRPROFILECREATE,
payload: drprofile,
}),
)
}
When I view the data being uploaded, it shows the file with its details like name, size etc. But the same does not come in the controller. It shows a blank array.
public function drProfileCreate(Request $request){
$data = $request->all();
$response = [
'success' =>true,
'datax' =>'Dr Profile uploaded in Contorller. Check',
'data' => $data
];
return response()->json($response, 201);
}
Hence Iam not able to upload the image. Help please. Appreciated
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);
}