node笔记_express结合formidable实现前后端的文件上传

news/2024/7/19 12:37:13 标签: 笔记, express, node.js

文章目录

    • ⭐前言
    • ⭐安装http请求的文件解析依赖库
      • 💖 安装 formidable
      • 💖 node formidable接受formData上传参数
    • ⭐上传的页面搭建
      • 💖 vue2 element upload
      • 💖 node 渲染 上传文件
    • ⭐后端生成api上传文件到指定目录
      • 💖完整的代码块
      • 💖效果图
    • ⭐结束

⭐前言

大家好,我是yma16,本期分享node使用express结合formidable实现前后端联调的文件上传
往期文章
node_windows环境变量配置
node_npm发布包
linux_配置node
node_nvm安装配置
node笔记_http服务搭建(渲染html、json)
node笔记_读文件
node笔记_写文件
node笔记_连接mysql实现crud

⭐安装http请求的文件解析依赖库

💖 安装 formidable

$ npm install formidable

💖 node formidable接受formData上传参数

查看 readme的介绍
formidable

const hostname = '127.0.0.1';
const port = 3000;
const express = require("express");
const formidable = require('formidable');
const app = express();
app.listen(port, hostname, () => {
	console.log(`Server running at http://${hostname}:${port}/`);
});
app.get("/", (req, res) => {
	console.log(__dirname)
	res.json({
		code: 200,
		data: 'yma16 blog',
		msg: 'csdn node challenge'
	})
});

app.post('/uploadFile/action', (req, res, next) => {
	// parse a file upload
	const form = new formidable({multiples: true});
	// parse 解析
	form.parse(req, (err, fields, files) => {
		console.log('req', req)
		console.log('err,', err)
		console.log('fields,', fields)
		console.log('files,', files)
		if (err) {
			next(err);
			return;
		}
		// json返回
		res.json({
			fields,
			files
		});
	})
})

⭐上传的页面搭建

使用formData上传二进制文件

💖 vue2 element upload

这里使用vue2和element实现上传

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>elementui vue2 上传文件</title>
		<!-- vue2 生产环境版本,优化了尺寸和速度 -->
		<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
		<!-- 引入样式 -->
		<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
		<!-- 引入组件库 -->
		<script src="https://unpkg.com/element-ui/lib/index.js"></script>
		<!-- axios -->
		<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
	</head>
	<style>
		#app {
			position: absolute;
			height: 100vh;
			width: 100vw;
		}

		.upload-container {
			position: relative;
			width: 100%;
			height: 100%;
		}

		.upload-container-box {
			position: absolute;
			left: 50%;
			top: 50%;
			transform: translate(-50%, -50%);
		}

		.container-title {
			position: relative;
			width: 100%;
			font-size: 24px;
			font-weight: bold;
			text-align: center;
		}
	</style>
	<body>
		<div id="app">
			<div class="container-title">
				{{ message }}
			</div>
			<div>
				<!--      文件上传 先关闭自动上传-->
				<div class="upload-container-box">
					<template>
						<!--         :action="uploadForm.uploadUrl"-->
						<el-upload class="upload-demo" :accept="uploadForm.accept" ref="uploadRef" drag
							:onRemove="handleRemove" :onChange="handlChange" :beforeUpload="beforeUpload"
							:action="uploadForm.uploadUrl" :autoUpload="uploadForm.autoUpload"
							:fileList="uploadForm.fileList" list-type="picture" :httpRequest="designUpload">
							<i class="el-icon-upload"></i>
							<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
							<div class="el-upload__tip" slot="tip">只能上传单个png、jpg文件</div>
						</el-upload>
						<div style="text-align: center">
							<el-button type="primary" plain @click="submitBtn"
								style="margin-top:10px">上传到服务器</el-button>
						</div>
					</template>
				</div>
			</div>
		</div>

		<script type="text/javascript">
			// ELEMENT.Message
			const {Message}=Element;
			// instance
			const instanceVue = {
				el: '#app',
				name:'upload-component',
				data() {
					const uploadTypeForm = {
						text: ["jpg", "png", "jpeg", "svg"]
					};
					return {
						message: 'element 自定义上传',
						$message:Message,
						uploadForm: {
							autoUpload: false,
							accept: uploadTypeForm.text.join(","),
							uploadUrl: "http://localhost:3000/uploadFile/action", //上传的url 默认空
							fileList: []
						}
					};
				},
				methods: {
					/**
					 * 文件删除回调
					 */
					handleRemove(file, fileList) {
						this.uploadForm.fileList = fileList;
					},

					/**
					 * 选择文件时回调
					 */
					handlChange(file, fileList) {
						this.uploadForm.fileList = fileList;
					},
					//上传前的回调
					beforeUpload: function(file) {
						console.info("上传前的钩子", file);
						// 关闭自动上传
						return false
					},
					submitBtn() {
						if (this.uploadForm.fileList.length <= 0) {
							this.$message({
								message: "请先选择文件!",
								type: "error"
							});
						}
						this.$refs.uploadRef.submit(); //触发自定义上传
					},
					//自定义上传
					designUpload(params) {
						console.info("自定义上传", params);
						const that = this;
						const formData = new FormData();
						formData.append("file", params.file);
						const header = {
							"Content-Type": "mutipart/form-data"
						};
						//上传的后端接口
						const upLoadUrl = that.uploadForm.upLoadUrl;
						axios({
								url: upLoadUrl,
								method: "post",
								data: formData,
								headers: header
							})
							.then(res => {
								console.info("res", res);
								params.onSuccess(); //成功icon
								that.$message({
									message: "上传成功!",
									type: "success"
								});
							})
							.catch(r => {
								that.$message.error("上传失败!");
								throw Error(r);
							});
					}
				},
			}
			// 实例化
			new Vue(instanceVue);
		</script>
	</body>
</html>

上传file参数是二进制文件
file-upload

💖 node 渲染 上传文件

const hostname = '127.0.0.1';
const port = 3000;
const express = require("express");
const app = express();

app.listen(port,hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
// server your css as static
app.use(express.static(__dirname));

app.get("/", (req, res) => {
	res.json({
		code:200,
		data:'yma16 blog',
		msg:'csdn node challenge'
	})
});
app.get("/uploadFile", (req, res) => {
	res.sendFile(__dirname + "/html/upload.html");
});

渲染 upload.html
upload.html

⭐后端生成api上传文件到指定目录

  1. 先渲染html的上传页面
  2. 接受文件参数
  3. 处理文件名,避免重名,加上时间撮处理

💖完整的代码块

指定 media为上传路径,生成文件名加上时间搓

const hostname = '127.0.0.1';
const port = 3000;

const express = require("express");
const formidable = require('formidable');
const fs = require('fs')
const app = express();

app.listen(port, hostname, () => {
	console.log(`Server running at http://${hostname}:${port}/`);
});
// server your css as static
app.use(express.static(__dirname));

app.get("/", (req, res) => {
	console.log(__dirname)
	res.json({
		code: 200,
		data: 'yma16 blog',
		msg: 'csdn node challenge'
	})
});


app.get("/uploadFile", (req, res) => {
	console.log(__dirname)
	res.sendFile(__dirname + "/html/upload.html");
});

function generateFilename(oldFilename) {
	//将老的文件名拼上时间戳
	let d = new Date();
	let names = oldFilename.split(".");
	return `${names[0]}_${""+d.getFullYear() + (d.getMonth()+1) + d.getDate() +'_'+ d.getHours() + d.getMinutes() + d.getSeconds()}.${names[1]}`;
}

app.post('/uploadFile/action', (req, res, next) => {
	// parse a file upload
	// const form = new formidable({multiples: true});
	// api回调
	const form = new formidable.IncomingForm();
	//保持原有扩展名
	form.keepExtensions = true;
	//设置上传目录
	form.uploadDir = __dirname + "/media/";
	// parse 解析
	form.parse(req, async(err, fields, files) => {
		console.log('req', req)
		console.log('err,', err)
		console.log('fields,', fields)
		console.log('files,', files)
		if (err) {
			next(err);
			return;
		}
		try {
			console.log('files.file', files.file)
			// 默认名称
			const  originalFilename = files.file.originalFilename;
			// 默认的路径
			const filePath= files.file.filepath
			//重命名上传的文件
			fs.rename(filePath, form.uploadDir + generateFilename(originalFilename), err => {
				if (err) {
					console.log("重命名失败");
					console.log(err);
				} else {
					console.log("重命名成功!");
				}
			})
		} catch (r) {
			next(r)
			return
		}
		// json返回
		res.json({
			fields,
			files
		});
	})
})

得到的文件流对象
在这里插入图片描述

💖效果图

前端的接口联调
文件上传成功

后端node处理生成的文件

上传的文件

⭐结束

感谢阅读💖,如有不足欢迎指出!

blue-sky-water


http://www.niftyadmin.cn/n/322860.html

相关文章

centos上使用yum安装redis

使用yum install -y redis时报错&#xff0c;没有找到安装包 解决办法&#xff1a; 下载fedora的epel仓库&#xff0c;yum添加epel源&#xff1a;执行 yum install epel-release 回车 再次&#xff1a;yum install redis 回车 开启redis服务 service redis star…

基于JSP技术的猎头公司管理软件的设计和实现——内部事务部分(源代码+论文)

随着信息科学技术的飞速发展&#xff0c;人们逐渐意识到对信息管理软件的运用可以使日常工作更加方便、快捷和高效。论文详细论述了猎头公司管理软件内部事务部分的开发设计过程。软件采用JSP开发技术&#xff0c;Tomcat作容器&#xff0c; SQL Server 2000作数据库管理系统&am…

Java 中有哪些类型的流?

Java 中的流&#xff08;Stream&#xff09;分为两种类型&#xff1a;字节流和字符流。 字节流&#xff08;Byte Stream&#xff09; 字节流可以处理任何类型的数据&#xff0c;但是它们是以字节为单位进行操作的。Java 中提供了两种字节流&#xff1a;InputStream 和 Output…

vscode编译的时候:未定义标识符 thread

vscode编译的时候&#xff1a;未定义标识符 thread thread’ was not declared in this scope" 未定义标识符 thread 原因 MinGW GCC当前仍缺少标准C 11线程类的实现。 对于跨平台线程实现&#xff0c;GCC标准库依赖于gthreads / pthreads库。如果该库不可用&#xf…

Golang每日一练(leetDay0066) 有效电话号码、转置文件

目录 193. 有效电话号码 Valid Phone Numbers &#x1f31f; 194. 转置文件 Transpose File &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 193. 有效电话号…

linux中epoll+socket实战

目录 参考前言案例 一、epoll的基本使用首先是epoll_create函数&#xff1a;然后是epoll_ctl函数&#xff1a;最后是epoll_wait函数&#xff1a;关于ET&#xff08;边沿触发&#xff09;、LT&#xff08;水平触发&#xff09;两种工作模式可以得出这样的结论: 二、使用代码简易…

【Prompting】ChatGPT Prompt Engineering开发指南(3)

ChatGPT Prompt Engineering开发指南3 总结文字使用单词/句子/字符限制进行总结以运输和交付为重点进行总结以价格和价值为重点进行总结 尝试“extract”而不是“summarize”总结多个产品评论内容来源 本文承接上文&#xff1a;ChatGPT Prompt Engineering开发指南2&#xff0c…

C语言实现扫雷

总有一天你要一个人在暗夜中&#xff0c;向那座桥走过去 目录 一、文件及其对应代码 1.test.c 2.game.c 3.game.h 二、数组创建解析 1.创建两个数组的原因 2.预设数组较大的原因 三、计算周围雷的个数 四、向外扩展并延伸判断 扫雷游戏&#xff0c;相信大家都玩过&am…