技术栈
koa2 + react16.8
github 地址链接
项目步骤
前端开发
1.创建
react app
yarn create react-app my-app
2.用material-ui
编写页面,代码如下<form action="/upload" method="post" encType="multipart/form-data" style={{width:'100%'}}> <div> <input accept="image/*" className={classes.input} id="contained-button-file" multiple type="file" onChange={uploadImg} /> <label htmlFor="contained-button-file"> <Button variant="contained" component="span" className={classes.button} > 添加图片 </Button> </label> <GridList cellHeight={100} className={classes.gridList} cols={3}> {/*{console.log('html',values.images&&values.images)}*/} { values.images && values.images.map((item,index) => ( <GridListTile key={index} cols={item.cols || 1} data-index = {index} > <strong>{item.name}</strong> <a href="javascript:void(0)" className="upload-delete" title="删除" data-index = {index} onClick={deleteImg} >删除{index}</a> <br/> <img src={item.thumb} /> </GridListTile> ))} </GridList> </div> </form> { values.images &&values.images.length ? <Button variant="contained" color="primary" style={{height:'40px',width:'20%'}} className={classes.margin} onClick={() => handleUpload()}> 上传图片 </Button>:'' }
3.其中需要用的函数代码如下:
const classes = useStyles(); const [values, setValues] = React.useState({ age: '', images:[], uploadHistory:[], }); function uploadImg(e) { e.preventDefault(); let target = e.target; let files = target.files; let count = files.length; for (let i = 0; i < count; i++) { files[i].thumb = URL.createObjectURL(files[i]) } // convert to array //Array.prototype.slice.call(arguments)能将具有length属性的对象(key值为数字)转成数组。 // []是Array的示例,所以可以直接使用[].slice()方法。 files = Array.prototype.slice.call(files, 0); files = files.filter(function (file) { return /image/i.test(file.type) }); setValues(oldValues => ({ ...oldValues, images: values.images.concat(files) })); } const deleteImg = (e) => { let index = e.target.getAttribute('data-index'); let result = values.images.splice(index,1); setValues(oldValues => ({ ...oldValues, images: values.images })); }; function handleUpload() { for (let i = 0, file; file = values.images[i]; i++) { ((file) => { let xhr = new XMLHttpRequest(); if (xhr.upload) { // 上传中 console.log('上传中') xhr.upload.addEventListener("progress", (e) => { // handleProgress(file, e.loaded, e.total, i); }, false); // 文件上传成功或是失败 xhr.onreadystatechange = (e) => { if (xhr.readyState == 4) { if (xhr.status == 200) { console.log('handleSuccess' ,file ) // handleSuccess(file, xhr.responseText); // this.handleDeleteFile(file); if (!values.images.length) { //全部完毕 handleComplete(); console.log('全部上传完成!'); } } else { handleFailure(file, xhr.responseText); console.log('上传出错!'); } } }; const form = new FormData(); form.append("filedata", file); // 开始上传 xhr.open("POST", "/upload", true); // xhr.setRequestHeader("FILENAME", file.name); console.log('form',form); xhr.send(form); } })(file) } } //当上传图片的时候,如果还需要从前端往后端传参数可以使用`form.append("filedata", file);`的方法
后端开发
1.koa搭建框架代码:
const Koa = require('koa');
const app = new Koa();
app.use(statics(
path.join(__dirname,staticPath)
));
app.use(statics('.'));
app.use(bodyParser());
app.listen(9000); //后端接听9000端口
2.前端引入静态文件,这样react,build好的文件直接和后端链接起来
const statics = require('koa-static'); //使用koa-static
const staticPath = './build';
app.use(statics(
path.join(__dirname,staticPath)
));
app.use(statics('.'));
3.get 和put 方法设置
app.use(async(ctx, next) => {
await next();
ctx.response.type = 'text/html';
// ctx.response.body = '<h1>Hello,koa2222!</h1>';
// console.log('ctx use',ctx.url,ctx.method);
if(ctx.method === 'GET'){ //当请求时GET请求时
ctx.body =ctx.response.body;
}else if(ctx.url==='/' && ctx.method === 'POST'){ //当请求时POST请求时
ctx.body=await parsePostData(ctx);
}else{
//其它请求显示404页面
ctx.body='<h1>404!</h1>';
}
});
function parsePostData( ctx ) {
return new Promise((resolve, reject) => {
try {
let postdata = "";
ctx.req.addListener('data', (data) => {
postdata += data
})
ctx.req.addListener("end",function(){
let parseData = parseQueryStr( postdata );
resolve( parseData )
})
} catch ( err ) {
reject(err)
}
})
}
function parseQueryStr( queryStr ) {
let queryData = {};
let queryStrList = queryStr.split('&');
console.log( queryStrList );
for ( let [ index, queryStr ] of queryStrList.entries() ) {
let itemList = queryStr.split('=');
queryData[ itemList[0] ] = decodeURIComponent(itemList[1])
}
return queryData
}
4.跨域设置
const cors = require('koa-cors');
app.use(cors());
5.图片上传api实现
router.post('/upload', async(ctx, next) => { //提交图片接口
var form = new multiparty.Form({uploadDir: './files/'});
// 上传完成后处理
form.parse(ctx.req, function(err, fields, files) {
//fields里有前端append的参数
if (err) {
throw err;
} else {
//form.append ()的值在fields中
processImg(ctx.req, ctx.res, files,fields).then(function(data) {
console.log('hshsh')
// 设置跨域
// allowCross(ctx);
// res.json({
// res: JSON.parse(data.filesTmp),
// relPath: data.relPath,
// })
}).catch(function(err) {
console.log(err)
});
}
});
});
function processImg(req, res, files,fields) {
return new Promise(function(resolve, reject) {
const _img = files.filedata[0];
const uploadedPath = _img.path;
const originalFilename = _img.originalFilename;
let dstPath = './imgs/'+ originalFilename;
// console.log('dstPath',dstPath);
// (目前的路径,重命名后的路径)重命名
fs.rename(uploadedPath, dstPath, function(err) {
if (err) {
reject(err)
} else {
console.log('rename ok!');
}
});
});
}
总结
前端:
1.material-ui
地址:https://material-ui.com/ 它是React组件,实现了谷歌Material Design设计规范。世界上最流行的React界面框架。
2.图片是file类型,需要转数组Array.prototype.slice.call
;
3.当上传图片的时候,如果还需要从前端往后端传参数可以使用form.append("filedata", file);
的方法
后端:
1.后端的form.parse
方法的fields
里有前端append
的参数;
2.跨域使用koa-cors
;
3.上传的图片名字不是图片本身的名字,可以fs.rename
来重新命名
4.get
和put
方法需要区分,可以使用上文中的代码。
5.koa-static
可以将react 中build后的文件同后端链接起来