Vue3 + Nodejs 实战 ,文件上传项目--实现图片上传

目录

技术栈

1. 项目搭建前期工作(不算太详细)

前端

 后端

2.配置基本的路由和静态页面

 3.完成图片上传的页面(imageUp)

静态页面搭建

 上传图片的接口

 js逻辑

4.编写上传图片的接口

5.测试效果

 结语


博客主页:専心_前端,javascript,mysql-CSDN博客

 系列专栏:vue3+nodejs 实战--文件上传

 前端代码仓库:jiangjunjie666/my-upload: vue3+nodejs 上传文件的项目,用于学习 (github.com)

 后端代码仓库:jiangjunjie666/my-upload-server: nodejs上传文件的后端 (github.com)

 欢迎关注

本系列记录vue3(前端)+nodejs(后端) 实现一个文件上传项目,目前只完成了图片的上传,后续会陆续完成:单文件上传,多文件上传,大文件分片上传,拖拽上传等功能,欢迎关注。

技术栈

前端:Vue3 Vue-router axios element-plus...

后端:nodejs express...

1. 项目搭建前期工作(不算太详细)

前端

我使用的是vite创建的vue项目,包管理器工具为:pnpm

pnpm create vite

创建好项目后安装依赖就可启动项目了

配置+安装需要用到的库

配置文件路径别名(在vite.config.js文件中)

安装需要用到的库

pnpm i vue-router
pnpm i element-plus
pnpm install @element-plus/icons-vue
pnpm i axios

进行基础配置(建好对应的文件夹)

导入Element-Plus (在main.js文件中)

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import ElementPlus from 'element-plus'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
//引入样式
import 'element-plus/dist/index.css'
import router from '@/router/index.js'const app = createApp(App)
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {app.component(key, component)
}
app.use(ElementPlus, {locale: zhCn
})
app.use(router)
app.mount('#app')

 后端

使用express框架快速搭建出node项目

npx express-generator

需要用到的依赖

npm i cors
npm i formidanle@2.1.2

在app.js文件中配置跨域

//配置跨域
var cors = require('cors')app.use(cors())

启动项目

npm start

2.配置基本的路由和静态页面

目前路由文件是这样的

//vue-router
// import Vue from 'vue'
import { createRouter, createWebHistory } from 'vue-router'const router = createRouter({history: createWebHistory(),routes: [{path: '/',//重定向至主页redirect: '/home'},{path: '/home',component: () => import('../views/home/index.vue'),name: 'home',redirect: '/home/imageUp',meta: {title: '首页'},children: [{path: '/home/imageUp',component: () => import('../views//home/imageUp/index.vue'),name: 'imageUp',meta: {title: '图片上传'}},{path: '/home/videoUp',component: () => import('../views/home/videoUp/index.vue'),name: 'videoUp',meta: {title: '视频上传'}},{path: '/home/fileUp',component: () => import('@/views/home/fileUp/index.vue'),name: 'fileUp',meta: {title: '文件上传'}}]}]
})export default router

目前就这几个页面

在App.vue中使用路由占位

home主页中的index.vue文件

这其中除了静态页面的搭建外,我使用了编程式路由跳转方式实现路由跳转,后续可能会添加更多功能(也可以使用其他的方式实现跳转,不唯一)。

<template><div class="container"><div class="top">My upload</div><div class="heart"><div class="left"><ul><li :class="{ active: activeIndex == 1 }" @click="changeUp('/home/imageUp', 1)"><el-icon size="20"><PictureFilled /></el-icon><p>图片上传</p></li><li :class="{ active: activeIndex == 2 }" @click="changeUp('/home/fileUp', 2)"><el-icon size="20"><FolderAdd /></el-icon><p>文件上传</p></li><li :class="{ active: activeIndex == 3 }" @click="changeUp('/home/videoUp', 3)"><el-icon size="20"><VideoCamera /></el-icon><p>视频上传</p></li></ul></div><div class="layout"><router-view></router-view></div></div></div>
</template><script setup>
import { ref } from 'vue'
//引入路由
import { useRouter } from 'vue-router'const $router = useRouter()
let activeIndex = ref(1)//二级路由跳转函数
const changeUp = (path, index) => {//路由跳转$router.push(path)activeIndex.value = index
}
</script><style lang="scss" scoped>
.container {.top {width: 100vw;height: 100px;background-color: rgb(61, 221, 154);text-align: center;font-size: 30px;color: #fff;line-height: 100px;}.heart {width: 100vw;//高度减去100pxheight: calc(100vh - 100px);display: flex;.left {width: 350px;height: 100%;background-color: #fffcfc;border-right: 1px solid #ccc;// padding-left: 20px;ul {li {width: 100%;height: 40px;display: flex;align-items: center;padding-left: 20px;p {margin-left: 10px;font-size: 16px;line-height: 40px;}}//给li加个active.active {color: rgb(61, 221, 154);}//加hoverli:hover {cursor: pointer;background-color: rgb(146, 236, 199);opacity: 0.8;color: black;}}}.layout {width: calc(100% - 350px);height: 100%;padding: 20px;}}
}
</style>

目前项目长这样:

 3.完成图片上传的页面(imageUp)

静态页面搭建

act用于控制上传图片时的不同状态:选择图片->上传中->上传成功

上传成功的图片会展示在下方的照片墙中

<template><div class="box"><div class="add"><input type="file" ref="fileInputRef" style="display: none" @change="handleFileChange" /><el-icon size="100" color="#ccc" v-if="act == 1" @click="openFileInput"><Plus /></el-icon><!-- loading效果 --><div class="loading" v-if="act == 2"></div><img :src="base64Img" alt="" v-if="act == 2" /></div></div><div class="imgList"><ul><li v-for="item in imgList"><img :src="item" alt="" /></li></ul></div>
</template><style lang="scss" scoped>
.box {width: 350px;height: 350px;border: 2px dashed rgb(175, 171, 171);border-radius: 2em;display: flex;justify-content: center;align-items: center;.add {position: relative;.loading {width: 100px;height: 100px;position: absolute;top: 35%;left: 35%;border: 3px solid #302b2b;border-top-color: transparent;border-radius: 50%;animation: circle infinite 0.75s linear;}// 转转转动画@keyframes circle {0% {transform: rotate(0);}100% {transform: rotate(360deg);}}img {width: 350px;height: 350px;z-index: -1;// 增加点模糊透明度opacity: 0.5;}}.add:hover {cursor: pointer;}
}
.imgList {width: 60%;// background-color: pink;margin-top: 30px;ul {border: 1px solid #ccc;border-radius: 20px;display: flex;flex-wrap: wrap;padding-left: 20px;li {width: 200px;height: 200px;margin: 5px 6px;// border: 1px solid pink;img {width: 100%;border-radius: 20px;height: 100%;}}}
}
</style>

 上传图片的接口

 js逻辑

我想实现的是有加载中的一个效果,但是单图片上传的速度,所以我使用了定时器来看到这个上传的效果,其中还没完成上传的图片,能显示加载出来主要是我们可以用FileReader中的onload事件来读取其中的数据,并会将其转为base64的数据,然后直接给img的src赋值,其他的就很简单了,代码基本能看懂。


<script setup>
import { ref } from 'vue'
import { reqUploadImg } from '@/api/data.js'
import { ElMessage } from 'element-plus'
let act = ref(1)
const fileInputRef = ref(null)
let selectedFile = ref(null)
let base64Img = ref('')
let imgList = ref([])
//上传函数
const openFileInput = () => {// 点击图标时触发文件选择框fileInputRef.value.click()
}const handleFileChange = async (event) => {// 处理文件选择事件act.value = 2selectedFile.value = event.target.files[0]// console.log(selectedFile.value)if (!selectedFile.value) {return}// 将图片转为base64显示loading状态const reader = new FileReader()reader.onload = (event) => {base64Img.value = event.target.result}reader.readAsDataURL(selectedFile.value) //调用生成base64// 创建一个FormData对象来包装文件const formData = new FormData()formData.append('file', selectedFile.value)// 使用上传图片的接口函数发送请求let res = await reqUploadImg(formData)if (res.code !== 200) {ElMessage({type: 'error',message: '上传失败'})act.value = 1return}//成功//开个定时器(为了看到上传的加载效果)else {let timer = setInterval(() => {act.value = 1clearInterval(timer)imgList.value.push(res.imgUrl)ElMessage({type: 'success',message: '上传成功'})}, 1000)}
}
</script>

到这里前端的功能基本完成了

4.编写上传图片的接口

先建好文件夹

路由images.js

var express = require('express')
var router = express.Router()const handler = require('./image_handler')
//挂载路由
router.post('/imageUpload', handler.imageUp)module.exports = router

接口函数

这里使用的包是formidable,下载的版本是2.1.2 ,如果版本不同可能代码会有所差异

这里限制了上传的图片类型和图片大小,并且将图片上传至了public/images文件夹中。

//放置上传图片的处理函数
//导入处理文件上传的包
const formidable = require('formidable')
const path = require('path')
exports.imageUp = (req, res, next) => {const form = formidable({multiples: true,uploadDir: path.join(__dirname, '../../public/images'),keepExtensions: true})form.parse(req, (err, fields, files) => {if (err) {next(err)return}console.log(files)//切割出上传的文件的后缀名let ext = files.file.mimetype.split('/')[1]//计算出图片文件大小let size = (files.file.size / 1024 / 1024).toFixed(2)if ((ext == 'png' || ext == 'jpg' || ext == 'jpeg') && size < 2) {let url = 'http://127.0.0.1:3000/images/' + files.file.newFilenameres.send({code: 200,msg: '上传成功',imgUrl: url})} else {res.send({code: 400,msg: '只能上传png、jpg、jpeg格式的图片或图片过大'})return}})
}

5.测试效果

上传中

上传成功

 后端文件夹中

 结语

下一篇,文件批量上传并显示实时传输进度条:Vue3 + Nodejs 实战 ,文件上传项目--实现文件批量上传(显示实时上传进度)_専心的博客-CSDN博客

如果没有接触过文件上传或者想尝试开发一下文件上传项目的可以交流学习一下,后续会陆续更新更多的功能,如有想法可在评论区交流或私信,感谢关注!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/108427.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TiDB 7.4 发版:正式兼容 MySQL 8.0

MySQL 是全球最受欢迎的开源数据库&#xff0c;长期位于 DB-Engines Ranking 排行榜第二名&#xff0c;在世界范围内拥有数量庞大的企业用户和开发者。然而&#xff0c;随着时间的推移&#xff0c;MySQL 用户正面临新挑战。Oracle 官宣将在 2023 年 10 月终止 MySQL 5.7 版本的…

2023,简历石沉大海?软件测试岗位真的已经饱和了....

各大互联网公司的接连裁员&#xff0c;政策限制的行业接连消失&#xff0c;让今年的求职雪上加霜&#xff0c;想躺平却没有资本&#xff0c;还有人说软件测试岗位饱和了&#xff0c;对此很多求职者深信不疑&#xff0c;因为投出去的简历回复的越来越少了。 另一面企业招人真的…

【halcon】halcon轮廓总结之select_contours_xld

前言 select_contours_xld 我认为是一个非常常用且实用的算子&#xff0c;用于对轮廓进行筛选。 简介 这段文档描述了一个名为"SelectContoursXld"的操作&#xff0c;用于根据不同特征选择XLD&#xff08;XLD是一种图像数据表示形式&#xff0c;表示轮廓线&#x…

竞赛 深度学习+python+opencv实现动物识别 - 图像识别

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 inception_v3网络5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; *…

Golang学习:基础知识篇(二)—— 数组及切片

Golang学习&#xff1a;基础知识篇&#xff08;二&#xff09;—— 数组及切片 前言什么是Golang&#xff1f;Go语言的基础语法数组声明数组初始化数组访问数组知识点补充 切片定义切片切片初始化len() 和 cap() 函数空(nil)切片切片截取append() 和 copy() 函数知识点补充 前言…

ubuntu20.04安装FTP服务

安装 sudo apt-get install vsftpd# 设置开机启动并启动ftp服务 systemctl enable vsftpd systemctl start vsftpd#查看其运行状态 systemctl status vsftpd #重启服务 systemctl restart vsftpdftp用户 sudo useradd -d /home/ftp/ftptest -m ftptest sudo passwd ftptest…

使用 Python 和蒙特卡罗计算未来股价走势以及历史波动率和隐含波动率

一、简介 预测金融市场是定量精度和全球经济细微差别的复杂融合。在这一探索中,蒙特卡罗模拟脱颖而出,成为首要的统计工具,指导我们对未来股票价格的理解。 这种方法以摩纳哥著名的蒙特卡洛赌场命名,并不依靠运气,而是植根于严格的概率模型。想象一下在受控环境中精心策划…

vue3后台管理框架之技术栈

vue3全家桶技术 基础构建&#xff1a; vue3vite4TypeScript 代码格式 &#xff1a; eslintprettystylelint git生命周期钩子&#xff1a; husky css预处理器&#xff1a; sass ui库&#xff1a; element-plus 模拟数据: mock 网络请求&#xff1a; axios 路由&#xff1a; vue…

SAP MM学习笔记37 - 请求书照合中的 追加请求/追加Credit 等概念/ 请求书的取消

有关请求书照合&#xff0c;之前学习了一部分&#xff0c;现在再来学其中的一些概念。 其实这些概念也许并不常用&#xff0c;但是你又不能不知道&#xff0c;因为客户会问。 有关请求书&#xff0c;贴一些以前学习的文章&#xff0c;以方便阅读。 SAP MM学习笔记33 - 请求书…

38.迪杰斯特拉(Dijkstra)算法

概述 我们在上一篇中面对修路的问题讲述了普利姆算法的实现方式&#xff0c;本篇我们参照迪杰斯特拉算法来对修路问题做进一步拆解。 我们回顾一下之前的问题&#xff1a; “要想富&#xff0c;先修路”&#xff0c;郝乡长最近为了德胜乡修路的事情愁白了头。 得胜乡有A、B、C…

《3D 数学基础》几何检测-最近点

目录 1. 直线上的最近点 2. 射线上的最近点 3. 点到平面的距离 4. 圆或球上的最近点 5. AABB上的最近点 1. 直线上的最近点 q是距离q的最近点&#xff0c;也就是q在直线上的投影。 其中p是直线上的点&#xff08;向量表示&#xff09;&#xff0c;n是直线的法向量&#x…

selenium教程 —— css定位

说明&#xff1a;本篇博客基于selenium 4.1.0 selenium-css定位 element_css driver.find_element(By.CSS_SELECTOR, css表达式) 复制代码 css定位说明 selenium中的css定位&#xff0c;实际是通过css选择器来定位到具体元素&#xff0c;css选择器来自于css语法 css定位优点…

史上最短的“牛熊转换”:BTC价格昨夜起飞,但却来自一条假新闻!

昨夜&#xff0c;加密市场经历了史上最短的一次“牛熊转换”。 在短短10分钟内&#xff0c;BTC快速走出多根阳线&#xff0c;价格直接起飞&#xff0c;连续突破28000美元、29000美元、30000美元的整数关口&#xff0c;最高触及30535.8美元&#xff0c;涨幅近10%&#xff08;数据…

接口自动化测试之HttpRunner测试框架

引言 接口自动化测试的实现方案有很多&#xff0c;没有编程基础的可以使用 PostmanNewman 或 JmeterAnt 来实现&#xff0c;有编程基础的则可以结合自动化测试框架来实现。基于Python的测试框架有&#xff1a;Unittest、HttpRunner、Robot Framework、Pytest等&#xff0c;本文…

python接口自动化测试(六)-unittest-单个用例管理

前面五节主要介绍了环境搭建和requests库的使用&#xff0c;可以使用这些进行接口请求的发送。但是如何管理接口案例&#xff1f;返回结果如何自动校验&#xff1f;这些内容光靠上面五节是不行的&#xff0c;因此从本节开始我们引入python单元测试框架 unittest&#xff0c;用它…

OpenCV模板匹配实现银行卡数字识别

目录 1&#xff0c;项目流程 2&#xff0c;代码流程解读 2.1 导入工具包 2.2 设置参数 2.3 指定信用卡类型 2.4 展示图像 ​编辑 2.5 读取一个模板图像 2.6 转化为灰度图--------->再转化为二值图像 2.7 计算轮廓 ​编辑 2.8 导入我们要识别的图像&…

【力扣每日一题】2023.10.13 避免洪水泛滥

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 给我们一个一维数组&#xff0c;元素为0表示对应日期不下雨&#xff0c;非0则表示对应日期对应号的湖泊下雨&#xff0c;下雨之后会导致该…

Spacedrive:开源跨平台文件管理 | 开源日报 No.57

denoland/deno Stars: 91.2k License: MIT Deno 是一个简单、现代和安全的 JavaScript 和 TypeScript 运行时&#xff0c;使用 V8 引擎并用 Rust 构建。其主要功能包括&#xff1a; 默认情况下具有高度安全性&#xff0c;除非显式启用&#xff0c;否则无法访问文件、网络或环…

哪种烧录单片机的方法合适?

哪种烧录单片机的方法合适&#xff1f; 首先&#xff0c;让我们来探讨一下单片机烧录的方式。虽然单片机烧录程序的具体方法会因为单片机型号、然后很多小伙伴私我想要嵌入式资料&#xff0c;通宵总结整理后&#xff0c;我十年的经验和入门到高级的学习资料&#xff0c;只需一…

Android versionCode会变成指定数值加001、002、003等后缀

“活久见”—今天遇到个奇怪的问题&#xff0c;指定的versionCode最终在打包出来的apk中,versionCode变成&#xff08;指定数值 00X的形式&#xff09; 如下所示&#xff1a; 注&#xff1a;当前build.gradle中的versionCode为26 后来搜索代码&#xff0c;发现原来是这个配置导…