vue + elementui + nodejs 后端 文件上传功能

今天踩了很多文件上传的坑,特此记录总结,方便广大网友查看。

废话不多说直接开始

目录

      • elementui 上传文件组件
      • vue template 代码
          • ../../../api/index.js 中的 api.uploadFile
          • ../utils/request.js 中的 request
          • 如果你只是写demo , 可以直接用 axios.post 实现 ,不用引入自定义的 api.uploadFile
        • 将 vue template 作为组件使用
      • nodejs 服务器代码
        • 上传文件部分
        • 源码

elementui 上传文件组件

官方文档 : https://element.eleme.cn/#/zh-CN/component/upload

我们使用拖拽上传
vue + elementui + nodejs 后端 文件上传功能_第1张图片

vue template 代码

<template>
	<div>
		<el-upload :action="'none'" :auto-upload="false"  drag ref='upload'  :on-change="onChange" :on-remove="onRemove" multiple show-file-list>
			<i class="el-icon-upload">i>
			<div class="el-upload__text">将文件拖到此处,或<em>点击上传em>div>
			<div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过500kbdiv>
		el-upload>
		<el-button icon="el-icon-upload" type="primary" style="margin-top: 20px;" @click="submitUpload">上传el-button>
	div>
template>

<script>
	import config from '../../../../klcloud.config.js'
	import api from '../../../api/index.js'
 


	export default {
      
		props: {
      
			//设置文件保存的路径,作为参数传递进来
			path: String
		},
		data() {
      
			return {
      
				//文件列表
				fileList: [],
			}
		},
		methods: {
      
			//文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
			onChange(file,filelist){
      
				//file.raw 才是真实的 file 对象
				this.fileList.push(file.raw)
			},
			//文件列表移除文件时的钩子
			onRemove(file, fileList){
      
				//file.raw 才是真实的 file 对象
				this.fileList.splice(this.fileList.indexOf(file.raw),1)
			},
			//上传文件
			submitUpload() {
      
				console.log("提交文件")
				let formData = new FormData();
				// 向 formData 对象中添加文件
				this.fileList.forEach(file => {
      
					formData.append('file', file);
				})
				//设置文件保存路径
				// formData.append('path', this.path);
				api.uploadFile(formData)
					.then(r => {
      
						console.log(r)
					}).catch(e => {
      
						console.log(e)
					})
			},
		},
		mounted() {
      
		},
	}
script>

<style scoped>

style>


 

属性 说明
action 表单提交地址,必填,我这里写 none 代表我们全权管理提交功能,不需要组件自动提交
auto-upload 自动上传,我们不需要,所以设置false
multiple 多文件上传
show-file-list 是否显示已上传文件列表
…/…/…/api/index.js 中的 api.uploadFile
import request from '../utils/request.js'
export default {
     
	uploadFile(formData){
     
 		//request : 封装好的axios 对象 ,下面具体说明
		return new request({
     
			url: `/api/file/upload`,
			method: 'post',
			data:formData
			
		})
	}
}
…/utils/request.js 中的 request
import axios from 'axios'

const service = axios.create({
     
	baseURL:'http://localhost:8080', //这里是你基础url , url =  baseURL + request url
	timeout:20000,
})

export default service

如果你只是写demo , 可以直接用 axios.post 实现 ,不用引入自定义的 api.uploadFile

用此片段代替上面的 vue template 的 submitUpload 函数

...

let formData = new FormData();
// 向 formData 对象中添加文件
this.fileList.forEach(file => {
     
formData.append('file', file);
})
//设置文件保存路径
formData.append('path', this.path);
//url 是你提交服务器的接口
axios.post(url,formData)
	.then(r=>{
     	
	}).catch(e=>{
     	
	})

...

将 vue template 作为组件使用

...
<el-dialog :title="'上传文件到:  '+path" :visible.sync="uploadDialog">
	<uploadFile :path="path">uploadFile>
el-dialog>
...
<script>
	import uploadFile from './uploadFile.vue'
	export default {
      
		props: {
      
		},
		components:{
      
			uploadFile
		},
		data() {
      
			return {
      
			path:'' // 设置文件上传到服务器的位置,比如服务器下有 public 目录, 你可以在这里写 ./public/
			}
		}
	}
script>



nodejs 服务器代码

上传文件部分

//上传文件
app.post('/api/file/upload', (req, res) => {
     
	//利用multiparty中间件获取文件数据
	let uploadDir = './' //这个不用改,因为并不是保存在这个目录下,这只是作为中间目录,待会要重命名文件到指定目录的

	let form = new multiparty.Form()

	form.uploadDir = uploadDir
	form.keepExtensions = true; //是否保留后缀
	form.parse(req, function(err, fields, files) {
      //其中fields表示你提交的表单数据对象,files表示你提交的文件对象
		console.log("上传文件", fields)
		console.log("files", files)
		//这里是save_path 就是前端传回来的 path 字段,这个字段会被 multiparty 中间件解析到 fields 里面 ,这里的 fields 相当于 req.body 的意思
		let save_path = fields.path 

		if (err) {
     
			console.log(err)
			res.send(formatReq(0, "上传失败"))
		} else {
     

			let file_list = []
			if (!files.file) res.send(formatReq(0, "上传失败"))
			else {
     
				//所有文件重命名,(因为不重名的话是随机文件名)
				files.file.forEach(file => {
     
					/*
					 * file.path 文件路径
					 * save_path+originalFilename   指定上传的路径 + 原来的名字
					 */
					fs.rename(file.path, save_path + file.originalFilename, function(err) {
     
						if (err) {
     
							// console.log("重命名失败")
						} else {
     
							// console.log("重命名成功")
						}
					});

				})
				if (err) {
     
					console.log(err)
					res.send(formatReq(0, "上传失败"))
				} else {
     
					//返回所有上传的文件信息

					res.send(formatReq(1, "上传成功"))
				}
			}

		}
	})
 
})
})

源码

以下全部为 服务器端 demo 代码,仅供参考

var express = require('express');
var fs = require('fs');
var path = require('path');
var bodyParser = require('body-parser');
var app = express();
var multiparty = require("multiparty")

//使用我自己的一些配置,主要就是配置端口,还有公开文件的路径
var config = require('./dist/klcloud.config.js')


app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
     
	extended: false
}))


//添加请求头
app.all('*', function(req, res, next) {
     
	res.header("Access-Control-Allow-Origin", "*");
	res.header("Access-Control-Allow-Headers", "content-type");
	res.header("Access-Control-Allow-Methods", "GET,POST");

	next();
});

 

// 读取当前文件夹下的所有文件
function walk(currentDirPath, callback) {
     
	var list = []
	var fs = require('fs'),
		path = require('path');
	fs.readdir(currentDirPath, function(err, files) {
     
		if (err) {
     
			callback()
		} else {
     
			for (let i = 0; i < files.length; i++) {
     
				var filePath = path.join(currentDirPath, files[i]);
				var stat = fs.statSync(filePath);
				if (stat.isFile()) {
     
					//添加文件信息
					list.push({
     
						file_name: files[i],
						file_stat: stat,
						file_path: filePath.replace(/\\/g, "/"),
						is_file: true	//是否为文件
					});
 
				} else if (stat.isDirectory()) {
     
					list.push({
     
						file_name: files[i],
						file_stat: stat,
						file_path: filePath.replace(/\\/g, "/"),
						is_file: false
					});
				}
			}
			callback(list)
		}
	});
}

//统一格式化返回值
function formatReq(code, msg, data) {
     
	return {
     
		code: code,
		data: data,
		message: msg
	}
}

 
// // 访问静态资源
app.use(express.static(path.resolve(__dirname, './dist/')));
// 访问单页
app.get('/', function(req, res) {
     
	var html = fs.readFileSync(path.resolve(__dirname, './dist/index.html'), 'utf-8');
	res.writeHead(200, {
     "Content-Type": "text/html"});
	
	res.send(html);
});



//获取路径下的文件
app.post('/api/file/open', (req, res) => {
     
	
	console.log("获取路径下的文件", req.body);
	let file_type = req.body.path.substring(req.body.path.lastIndexOf('.') + 1, req.body.path.length)
	console.log(file_type)
	
	//如果是图片
	if (file_type.match(/(png|jpg|svg|gif|jpeg|ico)/)) {
     
		fs.readFile(req.body.path,'binary',function(err,data){
     
		    if(err){
     
		        console.log(err)
		    }else{
     
				
		        console.log('数据读取成功');
				const buffer = new Buffer.from(data, 'binary');
				let src = 'data:image/'+ file_type +';base64,' + buffer.toString('base64');
				res.send(formatReq(1, "获取成功", src))
		    }
		});
	} else {
     
		var content = fs.readFileSync(req.body.path, 'utf-8');
		res.send(formatReq(1, "获取成功", content))
		res.end()
	}


})

//获取路径下的所有文件
app.post('/api/file/list', (req, res) => {
     
	console.log("获取路径下的文件夹", req.body);
	walk(req.body.path, file_list => {
     
		res.send(formatReq(1, "获取成功", file_list))
		res.end()
	})
})

//上传文件
app.post('/api/file/upload', (req, res) => {
     
	//利用multiparty中间件获取文件数据
	let uploadDir = './' //这个不用改,因为并不是保存在这个目录下,这只是作为中间目录,待会要重命名文件到指定目录的

	let form = new multiparty.Form()

	form.uploadDir = uploadDir
	form.keepExtensions = true; //是否保留后缀
	form.parse(req, function(err, fields, files) {
      //其中fields表示你提交的表单数据对象,files表示你提交的文件对象
		console.log("上传文件", fields)
		console.log("files", files)
		//这里是save_path 就是前端传回来的 path 字段,这个字段会被 multiparty 中间件解析到 fields 里面 ,这里的 fields 相当于 req.body 的意思
		let save_path = fields.path 

		if (err) {
     
			console.log(err)
			res.send(formatReq(0, "上传失败"))
		} else {
     

			let file_list = []
			if (!files.file) res.send(formatReq(0, "上传失败"))
			else {
     
				//所有文件重命名,(因为不重名的话是随机文件名)
				files.file.forEach(file => {
     
					/*
					 * file.path 文件路径
					 * save_path+originalFilename   指定上传的路径 + 原来的名字
					 */
					fs.rename(file.path, save_path + file.originalFilename, function(err) {
     
						if (err) {
     
							// console.log("重命名失败")
						} else {
     
							// console.log("重命名成功")
						}
					});

				})
				if (err) {
     
					console.log(err)
					res.send(formatReq(0, "上传失败"))
				} else {
     
					//返回所有上传的文件信息

					res.send(formatReq(1, "上传成功"))
				}
			}

		}
	})
 
})

//创建文件夹
app.post('/api/folder/create', (req, res) => {
     
	console.log("创建文件夹", req.body);
	fs.mkdir(req.body.path, (err) => {
     
		if (err) {
     
			console.log(err);
			res.send(formatReq(0, "创建文件夹失败"))
		} else {
     
			res.send(formatReq(1, "创建文件夹成功"))
		}
		res.end()
	});

})

//创建文件
app.post('/api/file/create', (req, res) => {
     
	console.log("创建文件", req.body);
	fs.writeFile(req.body.path, "", (err) => {
     
		if (err) {
     
			console.log(err);
			res.send(formatReq(0, "创建文件失败"))
		} else {
     
			res.send(formatReq(1, "创建文件成功"))
		}
		res.end()
	});
})
//删除目录
app.post('/api/folder/remove', (req, res) => {
     
	console.log("删除目录", req.body);
	fs.rmdir(req.body.path, (err) => {
     
		if (err) {
     
			console.log(err);
			res.send(formatReq(0, "删除文件夹失败"))
		} else {
     
			res.send(formatReq(1, "删除文件夹成功"))
		}
		res.end()
	})
})


//删除文件
app.post('/api/file/remove', (req, res) => {
     
	console.log("删除文件", req.body);
	fs.unlink(req.body.path, (err) => {
     
		if (err) {
     
			console.log(err);
			res.send(formatReq(0, "删除文件失败"))
		} else {
     
			res.send(formatReq(1, "删除文件成功"))
		}
		res.end()
	})

})



app.get('/api/login/:account/:password', function(req, res) {
     
	if (req.params.account =='admin' && req.params.password == '132525') {
     
		res.send({
     
			code: 1,
			message: '登录成功'
		});
	} else {
     
		res.send({
     
			code: 0,
			message: '登录失败'
		});
	}
	res.end()

});

// 监听
app.listen(config.port, function() {
     
	console.log('success listen in port:' + config.port, '\nyour login info : ' + JSON.stringify(config.login));
});

你可能感兴趣的:(Vue,【基础】,vue,nodejs,upload,中间件,elementui)