手机商城网站开发/正规引流推广公司
目录
技术栈
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博客
如果没有接触过文件上传或者想尝试开发一下文件上传项目的可以交流学习一下,后续会陆续更新更多的功能,如有想法可在评论区交流或私信,感谢关注!!!
相关文章:

Vue3 + Nodejs 实战 ,文件上传项目--实现图片上传
目录 技术栈 1. 项目搭建前期工作(不算太详细) 前端 后端 2.配置基本的路由和静态页面 3.完成图片上传的页面(imageUp) 静态页面搭建 上传图片的接口 js逻辑 4.编写上传图片的接口 5.测试效果 结语 博客主页:専心_前端,javascript,mys…...

linux C++ vscode连接mysql
1.linux使用Ubuntu 2.Ubuntu安装vscode 2.1 安装的是snap版本,直接打开命令行执行 sudo snap install --classic code 3.vscode配置C 3.1 直接在扩展中搜索C安装即可 我安装了C, Chinese, code runner, 安装都是同理 4.安装mysql sudo apt update sudo apt install mysql-…...

[资源推荐]langchain、LLM相关
之前很多次逛github或者去B站看东西或者说各种浏览资讯的情况,都会先看两眼然后收藏然后就吃灰的情况,那既然这样,不如多看几眼,看看是否真的能用得上,能用在哪,然后用几句话总结出来,分享出来&…...

hdfs笔记
1.HDFS shell 1.0查看帮助 hadoop fs -help <cmd> 1.1上传 hadoop fs -put <linux上文件> <hdfs上的路径> 1.2查看文件内容 hadoop fs -cat <hdfs上的路径> 1.3查看文件列表 hadoop fs -ls / 1.4…...

java_方法引用和构造器引用
文章目录 一、方法引用1.1、方法引用的理解1.2、格式1.3、举例 二、构造器引用2.1、格式2.2、例子2.3、数组引用 一、方法引用 1.1、方法引用的理解 方法引用,可以看做是基于lambda表达式的进一步刻画当需要提供一个函数式接口的实例时,可以使用lambda…...

Flink Log4j 2.x使用Filter过滤日志类型
Flink Log4j 2.x使用Filter过滤日志类型(区别INFO、ERROR) 文章目录 Flink Log4j 2.x使用Filter过滤日志类型(区别INFO、ERROR)ThresholdFilterLevelMatchFilter 日志级别: ALL < TRACE < DEBUG < INFO < …...

Ubuntu下怎么配置vsftpd
2023年10月12日,周四中午 目录 首先要添加一个系统用户然后设置这个系统用户的密码给新创建的系统用户创建主目录启动vsftpd服务查看vsftpd服务的状态打开外界访问vsftpd服务所需的端口获取服务器的IP地址大功告成 首先要添加一个系统用户 useradd 用户名然后设置…...

链表(7.27)
3.3 链表的实现 3.3.1头插 原理图: newnode为新创建的节点 实现: //头插 //让新节点指向原来的头指针(节点),即新节点位于开头 newnode->next plist; //再让头指针(节点)指向新节点&#…...

在 Elasticsearch 中实现自动完成功能 1:Prefix queries
自动完成与搜索功能不同 - 我们应该在用户键入下一个字符后立即更新自动完成选项,每秒都会访问数据库,过滤数百万条记录,而不会导致任何性能下降! Elasticsearch 是一种可以轻松实现此类功能的技术,它是一种基于 Apac…...

『PyQt5-Qt Designer篇』| 13 Qt Designer中如何给工具添加菜单和工具栏?
13 Qt Designer中如何给工具添加菜单和工具栏? 1 创建默认窗口2 添加菜单栏3 查看和调用1 创建默认窗口 当新创建一个窗口的时候,默认会显示有:菜单栏和状态栏,如下: 可以在菜单栏上右键-移除菜单栏: 可以在菜单栏上右键-移除状态栏: 2 添加菜单栏 在窗口上,右键-创建…...

Android Studio新建项目教程
Android Studio新建项目教程 一、创建新项目 二、选择空白页项目类型 配置然后finish 等待项目完成初试化 等待初始化结束,创建完成...

前端页面布局之【响应式布局】
目录 🌟前言🌟优点🌟缺点🌟media兼容性🌟利用CSS3-Media Query实现响应式布局🌟常见的媒体类型🌟常见的操作符🌟属性值🌟设备检测🌟响应式阈值选取dz…...

定制排序小案例
案例:自定义 Book 类,里面包含 name 和 price,按 price 排序(从大到小)。 要求使用两种方式排序 , 有一个 Book[] books 4 本书对象. 使用前面学习过的传递 实现 Comparator 接口匿名内部类,也称为定制排序。 可以按照 price …...

如何设计一个ToC的弹窗
本文主要分享了如何设计一个具有高可扩展性的弹窗功能。 本设计参考了优惠券功能的设计思路,有兴趣的朋友可以看看优惠券的分享:如何设计一个可扩展的优惠券功能_java优惠券系统设计-CSDN博客 一、需求介绍 假如你的项目需要实现以下弹窗,…...

Idea执行Pom.xml导入jar包提示sun.misc.BASE64Encoder jar找不到---SpringCloud工作笔记197
奇怪之前都是好好的,这个是因为,jdk的版本不对,重新打开以后自动被选择成jdk11了...记录一下 原因是从jdk9的时候,这个jar包已经被删除了,所以会报错,如果你用的是jdk自带的这个jar包就会报错,那么还可以,修改,不让他用jdk的,让他用 用org.apache.commons.codec.binary.Base64…...

大数据面试题:Spark和Flink的区别
面试题来源: 《大数据面试题 V4.0》 大数据面试题V3.0,523道题,679页,46w字 可回答:1)Spark Streaming和Flink的区别 问过的一些公司:杰创智能科技(2022.11),阿里蚂蚁(2022.11)&…...

2023年9月青少年软件编程(C 语言) 等级考试试卷(二级)
2023年9月青少年软件编程(C 语言) 等级考试试卷(二级) 编程题 1.数组指定部分逆序重放 题目描述 将一个数组中的前k项按逆序重新存放。 例如,将数组8,6,5,4,1前3项逆序重放得到5,6,8,4,1。 输入 输入为两行ÿ…...

【Wifi】Wifi架构介绍
Wifi架构介绍 本文基于Android介绍其Wifi架构。Wifi是许多操作系统提供的重要功能之一,特别是越来越多的车载系统wifi是其必备功能。为啥wifi是必备功能? 一方面是传统的上网(现在有些车载使用DCM模块管理网络),另一方…...

攻防世界数据逆向 2023
https://adworld.xctf.org.cn/contest/list?rwNmOdr1697354606875 目录 请求数据参数加密 cookie加密 响应数据解密 代码 请求数据参数加密 我们可以根据请求的关键字qmze1yzvhyzcyyjr获取到对应的加密地方 可以看到使用了函数_0x1dc70进行了加密 cookie加密 该步骤需…...

分布式链路追踪如何跨线程
背景 我们希望实现全链路信息,但是代码中一般都会异步的线程处理。 解决思路 我们可以对以前的 Runable 和 Callable 进行增强。 可以使用 ali 已经存在的实现方式。 TransmittableThreadLocal (TTL) 解决异步执行时上下文传递的问题 核心的实现思路如下&#…...

怎样在线修剪音频文件了?【免费,无须注册】
怎样在线修剪音频文件了? 推荐一个免费网址,且不用任何注册,直接可以使用 https://mp3cut.net/cn/ 上传音频文件, 拖动前后滚动条,对音频文件进行修剪。 修剪完成,可以保存如下格式 enjoy!! 作者简介…...

iMeta框架使用方法
📢📢📢📣📣📣 哈喽!大家好,我是「奇点」,江湖人称 singularity。刚工作几年,想和大家一同进步🤝🤝 一位上进心十足的【Java ToB端大厂…...

视频编辑软件 Premiere Pro 2024 macv24.0中文版 (pr2024)
Premiere Pro 2024 mac编辑任何现代格式的素材,从8K到虚拟现实。广泛的原生文件支持和简单的代理工作流程可以轻松使用您的媒体,即使在移动工作站上也是如此。提供针对任何屏幕或平台优化的内容比以往任何时候都快。 Premiere Pro 2024 Mac版软件介绍 视…...

C/C++:双向队列的实现
/** * * Althor:Hacker Hao * Create:2023.10.11 * */#include <bits/stdc.h> using namespace std; #define MAXSIZE 200 typedef struct Deque {int front; //头int rear; //尾int num; //队列中的元素数量int arr[MAXSIZE]; //队列中存储的数字 };Deque…...

MySQL逻辑架构
文章目录 逻辑架构剖析1. 连接层2. 服务层3. 引擎层4. 存储层 SQL执行流程1. MySQL中的 SQL执行流程(理论)2. MySQL8中的 SQL 执行流程(实践)确认profiling 是否开启多次执行相同SQL查询查看profiles查看profile 3. SQL语法顺序 数…...

python爬虫练手项目之获取某地企业名录
因为很多网站都增加了登录验证,所以需要添加一段利用cookies跳过登陆验证码的操作 import pandas as pd import requests from lxml import etree # 通过Chrome浏览器F12来获取cookies,agent,headers cookies {ssxmod_itna2:eqfx0DgQGQ0QGDC…...

Python —— 接口自动化(1)
1、接口测试的基础概述 1、接口测试的方式 1、主流的工具类型 - jmeter,postman,apifox,fastapi,apipost.... 2、公开的自动化平台 - metersphere,yapi.... 3、公司内部自研平台 - 4、全面使用代码自己去完成框架搭建,项目实战.... 不论是平台还是工具࿰…...

【MySQL】关于MySQL升级到8.0版本的实践方案
关于MySQL升级到8.0版本的实践方案 关于数据库版本升级,一直都是热议话题,对于升级的缘由各家也有所不同,有业务驱动的,有DBA自发驱动的,有规划导向也有方向指引的……抛开各种原因,当升级这个决定落下来的时候,对于DBA手头的几百几千套数据库来说,就好比是一场动物大…...

【Python-Django】基于TF-IDF算法的医疗推荐系统复现过程
复现步骤 step1: 修改原templates路径,删除,将setting.py中的路径置空 step2: 注册app python manage.py startapp [app名称]在app目录下创建static和templates目录 step3: 将项目中的资源文化进行拷贝 step4&#…...

车辆车型识别系统python+TensorFlow+Django网页界面+算法模型
一、介绍 车辆车型识别系统。本系统使用Python作为主要开发编程语言,通过TensorFlow搭建算法模型网络对收集到的多种车辆车型图片数据集进行训练,最后得到一个识别精度较高的模型文件。并基于该模型搭建Django框架的WEB网页端可视化操作界面。实现用户上…...