我正在尝试提供一种功能,让用户在登录时拥有默认的个人资料图片。然后他可以选择更新其个人资料图片。
我已经完成了 95%,但我只是停留在最后一步。
这个过程是这样的:
1) 登录后,个人资料图片将设置为默认图片。在 MYSQL 数据库中,我有一个 profilePic 字段设置为默认的 profilePic
2)当用户更改他/她的图片时,它会更改 profilePic 的状态
3) 当他们提交更改图片的请求(onSubmit 函数)时,它会将更新的图片上传到 AWS S3,并将数据库字段更新为他/她选择的任何更新的文件名。
但是,这会带来一个问题,因为当图片更改时,它会破坏我在那里的默认图片(因为我更改了状态),并且更改时 S3 中没有可用图片,它仅在提交时更改。问题是,我需要将文件名更改状态发送到服务器。
所以我的问题是,在他们提交并且图片在我的 S3 目录中可用并且进行数据库更改后,如何才能将其保存到何处,在提交发生之前该状态不会受到影响?
我想我可能需要使用生命周期方法,但我不是 100% 确定。
这是我的代码:
如果需要,我也会上传我的数据库查询和服务器代码。
查看:
import React from 'react';
import Header from '../common/Header';
import Base64 from 'base-64';
import { Modal, Button } from 'react-bootstrap';
import {Redirect} from 'react-router-dom';
import profileService from '../../services/Profilepage';
class Profilepage extends React.Component {
constructor(props){
super(props);
this.handleClose = this.handleClose.bind(this);
this.handleShow = this.handleShow.bind(this);
this.state = {
redirect: false,
firstName: '',
lastName: '',
email: '',
userName: '',
profilePic: '',
fileObject: '',
fileType: '',
fileSize: '',
filePayload: '',
errorMsg: false,
errorMsg2: false,
}
}
async userStatus(){
// Check if the user is logged in
if(localStorage.getItem('userData') === null) {
this.setState({redirect: true})
} else {
// Parse the data
const userObject = await JSON.parse(localStorage.getItem('userData'));
this.setState({
firstName: userObject.firstName,
lastName: userObject.lastName,
email: userObject.email,
userName: userObject.userName
})
}
}
handleClose(){
this.setState({
show:false,
// clear file state when modal closes
fileType: '',
fileSize: '',
fileObject: '',
filePayload: ''
});
}
handleShow(){
this.setState({
show:true
});
}
onChange(e){
var file = e.target.files[0];
var reader = new FileReader();
this.setState({
profilePic: file.name,
fileObject: e.target.files[0],
fileType: file.type,
fileSize: file.size,
});
reader.onload = (e) => {
this.setState({
filePayload: e.target.result
});
};
reader.readAsDataURL(file);
}
async onSubmit(e){
e.preventDefault();
console.log(this.state);
//Check if a uploaded photo was taken.
if(this.state.fileObject === ''){
this.setState({
errorMsg: true
})
}
else {
const updateData = {
userName: this.state.userName,
profilePic: this.state.profilePic,
fileType: this.state.fileType,
fileSize: this.state.fileSize,
filePayload: this.state.filePayload,
}
const updateprofileData = await profileService.updatePhoto(updateData)
//console.log(updateprofileData);
// Reload the page with users updated photo.
location.href='/Profilepage';
}
}
// Select users profilepic in database. Set it to state. However, this is what I'm stuck on cause if I change a picture with the onchange method, the picture will break because in S3, the picture isn't available until after the user has submitted the change.
async selectData(){
const selectData = {
profilePic: this.state.profilePic,
userName: this.state.userName
}
const selectprofileData = await profileService.selectPhoto(selectData)
this.setState({
profilePic: 'mys3linkishere' + this.state.profilePic
})
}
async componentDidMount(){
await this.userStatus();
await this.selectData();
}
render(){
if(this.state.redirect){
return(<Redirect to={'/'}/>)
}
return (
<div className="container">
<Header />
<div className="row">
<div className="col-md-5" id="profile-left">
<h1>Welcome, {this.state.userName}</h1>
<div className="img-rounded">
<img src={this.state.profilePic} alt="profile picture" />
</div>
<Button onClick={this.handleShow}>Upload New Photo</Button>
<Modal show={this.state.show} onHide={this.handleClose}>
<form
method="POST"
onSubmit={e => this.onSubmit(e)}
>
<Modal.Header closeButton>
<Modal.Title>Upload New Photo</Modal.Title>
</Modal.Header>
<Modal.Body>
{/* Please upload a photo. */}
{this.state.errorMsg === true ? <span style={{color: 'red'}}>Please upload a photo</span> : null}
<input
type="file"
className="form-control-file"
name="profilePic"
onChange={e => this.onChange(e)}
/>
</Modal.Body>
<Modal.Footer>
<Button type="submit" onClick={e => this.onSubmit(e)} bsStyle="primary">Upload Photo</Button>
</Modal.Footer>
</form>
</Modal>
</div>
</div>
</div>
)
}
}
export default Profilepage;
型号:
var db = require('../dbconnection');
var AWS = require('aws-sdk');
// AWS configuration
AWS.config.update({
accessKeyId: '....',
secretAccessKey: '....'
})
var profilePage = {
// Update users profilepic in database and insert photo in s3 bucket
updatePhoto: function(data, callback){
let uniqueprofilePic = data.userName + '-' + data.profilePic;
db.query('UPDATE users SET profilePic="'+uniqueprofilePic+'" WHERE userName="'+data.userName+'"')
//console.log(data);
var buf = new Buffer(data.filePayload.replace(/^data:image\/\w+;base64,/, ""),'base64');
var s3 = new AWS.S3();
var params = {
Bucket: 'mysimplebucket',
Key: uniqueprofilePic,
Body: buf,
ContentType: data.fileType,
ACL: 'public-read',
};
s3.putObject(params, function(err, data){
if(err) {
console.log(err, err.stack);
} else {
return data
console.log(data);
}
})
callback(true);
},
selectPhoto: function(data, callback){
db.query('SELECT * from users WHERE userName="'+data.userName+'"', callback)
}
}
module.exports = profilePage;
最佳答案
好吧,我理解这种情况的方式是,当调用 onChange 时,您将 this.state.profilePic
state 设置为文件名。因此,当再次调用 render 方法时,img
属性将查找文件的地址。相反,它只是获取文件名。
如果您想保持现状,那么我建议您更新 onChange 方法以包含将文件上传到 S3 的方法调用(在 Onchange 中 setState
之前),并确保网络往返在 seState
之前完成。然后您可以在 onSubmit 期间更新您的数据库。我个人觉得这个解决方案有点令人讨厌,但它会完成工作。
或者
您可以在 onChange 期间将完整的本地文件路径设置为
我意识到 Chrome 不允许您设置本地文件路径。this.state.profilePic
的值。
像这样编辑 onChange 函数应该可以解决它 -
reader.onload = (e) => {
this.setState({
filePayload: e.target.result,
profilePic: reader.result
});
};
并从之前的几行中删除 setState。 如果有效请告诉我!
关于node.js - 如何仅在提交更改后而不是更改期间提交更新的照片?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50106939/