vue+Nodejs+Koa搭建前后端系统(九)-- 上传图片

web2.0的到来使网页世界正式进入了寒武纪,各式各样的多媒体资源屡见不鲜,上传资源变得刻不容缓!

前言

本文是在该系列的基础上,针对前后端代码的修改。

准备

HTTP上传图片时Content-Type值常见的有2种:application/jsonmultipart/form-data

前端准备

修改axios配置

/** /src/https.ts */
const http: MyAxiosInstance = axios.create({baseURL: process.env.NODE_ENV === 'production' ? httpHost : '/nodeApi/',/**New Code Start*/headers: {'Content-Type': 'application/json',//将axiox的Content-Type值默认为application/json},/**New Code End*/timeout: 60000,
});/**其他代码省略**//**请求拦截器 */
http.interceptors.request.use(function (config) {const token = window.localStorage.getItem('token');const username = userStore.userName;if (token) {config.headers.authorization = token;}/**New Code Start*///若请求Content-Type值为application/json,则处理请求数据if (config.headers['Content-Type'] === 'application/json' && username && config.data) {/**New Code End*/try {config.data = { username: username, ...config.data }} catch (e) {console.error(`请求拦截error:${e}`)}}return config;
}, function (error) {return Promise.reject(error);
});

上述代码修改了请求拦截,即:若请求类型为application/json时,则处理;否则(主要是针对multipart/form-data),不处理。

后端准备

1.安装koa-body

npm install koa-body

2.安装hexoid

npm install --save hexoid

3.安装dayjs

npm install dayjs

4.新建路由文件 /routes/files.js (用于接收上传图片请求并处理)

在这里插入图片描述

5.新建 /public/uploads/ 目录,用于存放上传的图片

在这里插入图片描述

数据库准备(非必须)

新建一张用于存储上传图片信息的表

CREATE TABLE user_uploads(  src VARCHAR(510) NOT NULL PRIMARY KEY COMMENT '文件地址',create_time TIMESTAMP NOT NULL COMMENT '上传时间',title VARCHAR(255) COMMENT '文件标题',description VARCHAR(1000)  COMMENT '文件描述',username VARCHAR(20) COMMENT '上传人',group_by VARCHAR(20) COMMENT '分组',mimetype VARCHAR(255) COMMENT 'MIME类型'
) COMMENT '文件上传维护表';

对之前代码修改

为了更加工整一些,作出如下修改:

1.修改create_user 表

#删除create_user 表的id列
ALTER TABLE create_user DROP COLUMN id;
#设置create_user 表的username列为主键
ALTER TABLE create_user ADD PRIMARY KEY (username);

修改后的表结构

在这里插入图片描述

2.后端代码新增配置页
为了统一后端的一些配置项,在后端项目根目录新建index.config.js文件
在这里插入图片描述
代码为:

/** /index.config.js */
module.exports = {//不验证用户名的接口no_verify: ["/login/loginIn", "/token/refresh", "/users/register", "/files/upload"],//静态文件夹路径static_basepath:'./public',//上传文件存储的目录static_uploadpath:'uploads'
}

然后修改引用的地方

/** /app.js */
/** 其他代码省略 */
const { verifyToken } = require("./middleware/jwt_copy1.js");
const { no_verify, static_basepath } = require("./index.config.js");app.use(require("koa-static")(path.join(static_basepath)));//__dirname + "/public"
app.use(verifyToken({ no_verify: no_verify }));

前端处理上传图片的几种方式

使用 FormData 对象

可以使用原生form标签,实现FormData 对象上传图片

<!--index.vue-->
<form action="/nodeApi/files/upload" method="POST" enctype="multipart/form-data" target="_blank"><input type="file" name="file" multiple /><input type="hidden" name="username" value="xiaoyang" /><button type="submit">上传</button>
</form>

但该方法有默认行为,比如上传成功后会跳转页面,而且不够灵活。鉴于此,可用js配合,使其更加灵活:

<!--index.vue-->
<template><input type="file" @change="getFiles" /><img v-for="i in previewImgs" :key="i" :src="i" /><button @click="upload">上传</button>
</template>
<script setup lang="ts">
import { reactive, ref } from "vue";
import http from "@/http";
import { ElMessage } from "element-plus";//要上传图片的File对象数组,用于提交后台
const files = ref<any>([]);
//要上传图片的DataUrl数组,用于预览图片(这里也可以用BlobUrl)
const previewImgs = ref<(string | ArrayBuffer)[]>([]);const getFiles = (e: any) => {files.value = files.value.concat([...e.target.files]);files.value.forEach((file: File, index: number) => {const reader = new FileReader();reader.readAsDataURL(file);reader.addEventListener("load", () => {previewImgs.value[index] = reader.result;});});
};
const upload = () => {//创建formData对象const formData = new FormData();//给formData对象添加files[]成员,多图片for (let i in files.value) {formData.append("files[]", files.value[i]);}formData.append("username", 'xyz');http.post("files/upload", formData, {headers: {"Content-Type": "multipart/form-data",},}).then((data: any) => {ElMessage({message: "上传成功",type: "success",});}).catch((err: any) => {ElMessage({message: err.message,type: "error",});});
};
</script>

使用 Base64 编码

这种方法将文件转换成 Base64 编码的字符串,然后通过普通的 JSON 格式发送给服务器。这种方式适用于较小的文件,因为 Base64 编码会增加数据大小。

<!--index.vue-->
<template><input type="file" @change="getFiles" multiple /><button @click="upload">上传</button>
</template>
<script setup lang="ts">
import { reactive, ref } from "vue";
import http from "@/http";
import { ElMessage } from "element-plus";//要上传图片的File对象数组,用于提交后台
const files = ref<any>([]);const getFiles = (e: any) => {files.value = files.value.concat([...e.target.files]);
};
/**
*** 将文件转换为base64
*** files 文件数组,File[]
*** callback 解析完成的回调方法,该回调方法第一个参数是解析的base64数组,第二个参数是对应的文件名称数组
*/
const readFiles = (files: File[] = [], callback: (f: string[],n:string[]) => void) => {const len = files.length;let readNum = 0;const results = [];for (let i = 0; i < len; i++) {const reader = new FileReader();reader.onload = (e: any) => {readNum++;results.push(e.target.result);if (readNum === len) {callback(results,files.map((item: any) => item.name));}};reader.onerror = () => {readNum++;if (readNum === len) {callback(results,files.map((item: any) => item.name));}};reader.readAsDataURL(files[i]);}
};
const upload = () => {readFiles(files.value, (base64List: string[], filenameList: string[]) => {http.post("files/upload", {files: base64List,filename: filenameList,}).then((data: any) => {ElMessage({message: "上传成功",type: "success",});}).catch((err: any) => {ElMessage({message: err.message,type: "error",});});});
};
</script>

后端处理上传图片

1.编写路由文件

/** /routes/files.js */
const path = require("path");
const { uploadFiles, removeFiles } = require("../module/files")
const { koaBody } = require('koa-body');
const router = require('koa-router')();
const { jsonable } = require('../middleware/files')
const fs = require("fs");
const { static_basepath, static_uploadpath } = require("../index.config");
const dayjs = require('dayjs');
/** 上传图片 */
router.post('/upload', async (ctx, next) => {const content_type = ctx.request.header['content-type']if (content_type === 'application/json') {return jsonable()(ctx, next);} else if (content_type.includes('multipart/form-data')) {return koaBody({// 支持多文件格式multipart: true,formidable: {// 上传目录uploadDir: path.join(static_basepath, static_uploadpath),// 保留文件扩展名keepExtensions: true,//图片上传前的事件句柄onFileBegin(formName, file) {ctx.request.formName = formName;//上传图片的根目录const dirpath = path.join(static_basepath, static_uploadpath, dirname);const D = new Date();//上传图片的二级目录(以‘年月日’格式为目录名)const dirname = dayjs(D).format("YYYYMMDD");//检查上传图片的目录是否存在,若不存在,则创建if (!fs.existsSync(dirpath)) {fs.mkdirSync(dirpath);}//改写文件存储的路径file.filepath = path.join(static_basepath, static_uploadpath, dirname, file.newFilename)}}})(ctx, next)}
}, uploadFiles);
module.exports = router;

此处上传图片采用koa-router中间件router.post(path,middleware,callback)格式,middleware是中间件,该中间件代码逻辑为:

判断请求的content-type值,若为application/json,利用自定义的jsonable中间件处理上传,若为multipart/form-data,利用koa-body中间件处理上传。上传图片完成后执行uploadFiles回调函出,该函数用来处理数据库。

上传的图片如图:

在这里插入图片描述

2.编写自定义的jsonable中间件处理application/json的请求

新建 /middleware/files.js 文件,代码如下:

/** /middleware/files.js */
const path = require("path");
const fs = require("fs");
const { static_basepath, static_uploadpath } = require("../index.config");
const dayjs = require('dayjs');
const hexoid = require('hexoid');
/** 生成上传图片的目录(若没有该目录则创建) */
function fileDir() {const D = new Date();const dirname = dayjs(D).format("YYYYMMDD");const dirpath = path.join(static_basepath, static_uploadpath, dirname)if (!fs.existsSync(dirpath)) {fs.mkdirSync(dirpath);}return dirpath;
}
function jsonable() {return async (ctx, next) => {//请求参数const params = ctx.request.body;//生成上传图片的目录const dirpath = fileDir();//获取文件数组const files = Array.isArray(params.files) ? params.files : [params.files];//获取文件名称数组const filenames = params.filename || [];//当前被处理的文件let file = null;//存储图片信息的数组const filepathArr = [];//循环处理图片(该图片为base64数据)while (file = files.shift()) {//当前循环的文件名const filename = filenames.shift() || '.png';//生成上传图片的名称--为25为的随机名称const uuid = hexoid(25)();//图片base64数据主体const base64Data = file.split('base64,')//图片的MIMEType,即Content-Typeconst mimetype = base64Data[0].replace(/^data:(.+?);/, '$1');//图片扩展文件名const ext = '.' + filename.split(".").reverse()[0];//图片要存储的路径const filepath = path.join(dirpath, uuid + ext);//将图片上传到指定路径fs.writeFileSync(filepath, Buffer.from(base64Data[1], 'base64'));//获取图片信息对象const f = fs.statSync(filepath)filepathArr.push({ filepath: filepath, mimetype, lastModifiedDate: f.birthtime })}/** 改写请求参数以备后续使用(这里是模拟koa-body) Start*/ctx.request.formName = 'file[]';ctx.request.files = { [ctx.request.formName]: filepathArr };/** 改写请求参数以备后续使用(这里是模拟koa-body) End*/return next();}
}
module.exports = {jsonable,
}

该中间件用于处理Content-Type为application/json的上传图片请求,图片格式须为base64,可以是多图上传。

3.编写上传图片完成后的回调方法

新建 /module/files.js 文件,其代码如下:

/** /module/files.js */
const fs = require('fs')
const path = require("path")
const os = require("os");
const { URL } = require("url");
const { static_basepath } = require("../index.config");
const dayjs = require('dayjs');
/** 获取本机IPv4地址 */
function getIPAdress() {var interfaces = os.networkInterfaces();for (var devName in interfaces) {var iface = interfaces[devName];for (var i = 0; i < iface.length; i++) {var alias = iface[i];if (alias.family === "IPv4" &&alias.address !== "127.0.0.1" &&!alias.internal) {return alias.address;}}}
}
/** 对于上传的图片写入数据库 */
async function uploadFiles(ctx, next) {const files = ctx.request.files;const formName = ctx.request.formName;const f = files[formName];const params = ctx.request.bodyconst host = `http://${getIPAdress()}:${process.env.PORT}/`//用于存储sql表信息的数组const v = [];if (Array.isArray(f)) {//多文件f.forEach((item) => {const url = `${host}${path.relative(static_basepath, item.filepath)}`;const myURL = new URL(url);v.push({username: params?.username || "",//上传图片的用户create_time: dayjs(item.lastModifiedDate).format("YYYY-MM-DD HH:mm:ss"),//上传时间src: myURL.pathname,//上传路径mimetype: item.mimetype,//MimeType值 group_by: params?.group || "",//组})})} else {//单文件const url = `${host}${path.relative(static_basepath, f.filepath)}`;const myURL = new URL(url);v.push({username: params?.username || "",create_time: dayjs(f.lastModifiedDate).format("YYYY-MM-DD HH:mm:ss"),src: myURL.pathname,mimetype: f.mimetype,group_by: params?.group || "",})}//整合sql语句const keys = Object.keys(v[0]);const columns = keys.join(",");const values = v.map(item => `(${keys.map(key => typeof item[key] === "string" ? `'${item[key]}'` : item[key]).join(",")})`)const sql = `INSERT INTO user_uploads (${columns}) VALUES ${values.join(",")}`;//执行sql语句并响应请求try {const r = await ctx.db.query(sql);ctx.response.status = 200;ctx.body = { message: "上传成功", code: 0, data: { path: v.map(item => item.src).join(",") } };} catch (e) {ctx.response.status = 500;ctx.body = { message: e, code: 99 };}
}
module.exports = {uploadFiles
};

uploadFiles回调方法主要作用就是将上传的图片写入数据库,数据库表如下:

在这里插入图片描述
这里对于上传图片信息写入数据库是非必须的,这里我只是为了后续做图片管理打基础。

总结下我这里后端处理上传图片的逻辑:
1.在koa-router的中间件参数中处理图片上传,在其回调函数参数中处理上传图片的数据库
2.在处理图片上传中,根据请求数据的Content-Type给以不同的处理方案

后端处理删除图片

1.编写路由文件

/routes/files.js 文件中添加如下代码:

/** /routes/files.js */
/**  其他代码省略... */
router.post('/remove', removeFiles);
module.exports = router;

2.编写删除图片回调方法

/module/files.js 文件中添加如下代码:

/** /module/files.js */
/** 
*** 查询该用户是否有删除该图片的权限--目前规则是只有上传者才有删除权限 
*** paths 要删除的图片路径,String类型,多个时用逗号分隔
*** username 用户名
*/
function filePermissions(ctx, next, paths, username) {const pathArr = paths.split(',');//将传来的paths去掉域名const pathSql = pathArr.map(item => `'${item.replace(/^(http|https):\/\/[^\/]+/, "").replace(/^\s+|\s+$/gim, "")}'`).join(",")//sql:筛选paths的用户名和路径集合const sql = `SELECT src,username FROM user_uploads WHERE src in (${pathSql})`;return new Promise(async (resolve, reject) => {try {const r = await ctx.db.query(sql);if (r) {//若路径存在,则继续const v = r.filter(item => item.username !== username);//只要有一个权限不对,则拒绝,并将拒绝的sql行返回,否则,返回图片路径if (v.length) {reject(v)} else {resolve(r.map(item => item.src))}} else {//若路径都不存在resolve('未查到该文件权限')}} catch (e) {reject(e)}})
}
/**
*** 删除服务器中的图片(物理删除),并将
*** fileArr 要删除图片路径,数组
*/
function delFile(fileArr = []) {return new Promise((resolve, reject) => {let t = "";const d = [];const errInfo = {atleastOneSucces: false,//true 至少一个删除成功notExit: false,//至少一个文件不存在otherExit: false,//删除文件时至少一个文件发生其他错误}/** 循环删除文件 */while (t = fileArr.pop()) {const delPath = path.join(static_basepath, t);try {/*** @des 判断文件或文件夹是否存在*/if (fs.existsSync(delPath)) {fs.unlinkSync(delPath);d.push({ message: "删除成功", path: t })errInfo.atleastOneSucces = true;} else {d.push({ errMsg: "文件不存在", path: t });errInfo.notExit = true;}} catch (error) {d.push({ errMsg: error, path: t });errInfo.otherExit = true;}}if (errInfo.atleastOneSucces) {if (errInfo.notExit|| errInfo.otherExit) {return resolve({ message: "部分删除成功", code: 2, data: d });} else {return resolve({ message: "删除成功", code: 0, data: d });}} else if (!errInfo.atleastOneSucces) {return reject({ message: "删除失败", code: 3, data: d });}})
}
/** 
***在数据库删除图片信息
***pathArr 要删除图片路径,数组
*/
function delUserUploadsTableRow(ctx, next, pathArr) {const pathSql = pathArr.map(item => `'${item.replace(/^(http|https):\/\/[^\/]+/, "").replace(/^\s+|\s+$/gim, "")}'`).join(",");const sql = `DELETE FROM user_uploads WHERE src in (${pathSql})`;return new Promise(async (resolve, reject) => {try {await ctx.db.query(sql);resolve(true);} catch (e) {reject(e)}})
}
/** 删除图片总方法 */
async function removeFiles(ctx, next) {const params = ctx.request.body;//要删除的图片路径const paths = params.path;//当前操作用户const username = params.username;let filepathArr = [];/** 查看当前用户是否有权限 START*/try {filepathArr = await filePermissions(ctx, next, paths, username)} catch (e) {if (Array.isArray(e)) {ctx.response.status = 403;ctx.body = { message: '没有权限', code: 1, data: e };} else {ctx.response.status = 500;ctx.body = { message: e, code: 99 };}return;}/** 查看当前用户是否有权限 END*//** 物理删除图片 START*///存储删除信息let fileDeleteInfo;try {fileDeleteInfo = await delFile(filepathArr);} catch (e) {fileDeleteInfo = e;ctx.response.status = 500;ctx.body = { message: e, code: 99 };return;}/** 物理删除图片 END*//** 在数据库删除已物理删除图片的信息 START*/try {const successDelArr = fileDeleteInfo.data.filter(item => item.message).map(item => item.path);await delUserUploadsTableRow(ctx, next, successDelArr);} catch (e) {fileDeleteInfo = { message: e, code: 4 };}/** 在数据库删除已物理删除图片的信息 END*/ctx.body = fileDeleteInfo;
}

番外

formidable.js

上面后端代码处理formData数据使用的是koa-body中间件,而koa-body是基于formidable.js,那么就可以使用formidable.js替代koa-body:

安装formidable.js

npm install formidable@v3

代码如下:

/** /routes/files.js */
const path = require("path");
const { uploadFiles, removeFiles } = require("../module/files")
const router = require('koa-router')();
const { IncomingForm } = require('formidable');//引入formidable
const { jsonable } = require('../middleware/files')
const fs = require("fs");
const { static_basepath, static_uploadpath } = require("../index.config");
const dayjs = require('dayjs');router.post('/upload', async (ctx, next) => {const content_type = ctx.request.header['content-type']if (content_type === 'application/json') {return jsonable()(ctx, next);} else if (content_type.includes('multipart/form-data')) {/** formidable插件使用 START *///配置项const p = {//支持多文件上传multipart: true,// 上传目录uploadDir: path.join(__dirname, '../public/uploads'),// 保留文件扩展名keepExtensions: true,}const form = new IncomingForm(p);//上传前的事件form.on('fileBegin', (formName, file) => {ctx.request.formName = formName;const dirname = dayjs().format("YYYYMMDD");const dirpath = path.join(static_basepath, static_uploadpath, dirname)if (!fs.existsSync(dirpath)) {fs.mkdirSync(dirpath);}file.filepath = path.join(static_basepath, static_uploadpath, dirname, file.newFilename)})await new Promise((resolve, reject) => {//解析Node.js Request对象form.parse(ctx.req, (err, fields, files) => {if (err) {reject(err);return;}ctx.request.files = files;ctx.request.body = {};for (let i in fields) {ctx.request.body[i] = fields[i][0]}resolve({ fields, files });});});return next();/** formidable插件使用 START */}
}, uploadFiles);
router.post('/remove', removeFiles)
module.exports = router;

本文实现的后端方法不仅能上传图片,也可以上传其他文件,且可以多文件上传。

结语

新纪元的到来让世界勃勃生机,而角角落落依然遍布着草履虫们,因为它们仍旧适应这个世界。

参考资料

Axios 中怎么上传文件(Upload File)?上传方法有哪几种?
思否:在Koa.js中实现文件上传的接口
简书:NodeJs koa2实现文件上传
CSDN:koa-body koa2 使用 koa-body 代替 koa-bodyparser 和 koa-multer
CSDN:koa-body4接收formData数据
CSDN:koa-body 文件上传自定义文件夹及文件名称
稀土掘金:原生nodejs 处理文件上传
简书:node原生处理前端传送的数据
稀土掘金:上传文件时获取上传进度
W3cways:koa + formidable实现文件上传
npm:koa-body
npm:hexoid
npm:node-mime-types
CitCode–GitHub镜像

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

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

相关文章

解决:chrome无痕模式下找不到插件问题

如需在 Chrome 浏览器或 ChromeOS 设备上以无痕模式使用扩展程序&#xff0c;请执行以下操作&#xff1a; 在新的无痕式窗口中&#xff0c;打开 chrome://extensions。找到要在无痕模式下使用的扩展程序。点击详情。开启在无痕模式下启用。 此时打开无痕模式&#xff0c;就能看…

经营分析怎么做?详解企业月度经营分析的思路与方法

企业经营分析是企业成功的关键之一&#xff0c;无论企业规模大小&#xff0c;都需要通过系统性的数据分析来指导经营决策。这一过程不仅仅是对集团大局数据的简单处理&#xff0c;还包括对市场、客户、生产、财务、运营、项目进展、人效等多个方面数据的全面审视。通过深入分析…

【微信小程序】传参存储

目录 一、本地数据存储 wx.setStorage wx.setStorageSync 1.1、异步缓存 存取数据 1.2、同步缓存 存取数据 二、使用url跳转路径携带参数 2.1、 wx.redirectTo({}) 2.2、 wx.navigateTo({}) 2.3、 wx.switchTab({}) 2.4 、wx.reLaunch({}) 2.5、组件跳转 三、…

解压常见_gzip:stdin:not in gzio format:怀疑下文件是否损坏

此次的主角文件是&#xff1a;pin-2.14-71313-gcc.4.4.7-linux.tar.gz 结论&#xff1a;文件后缀没问题&#xff0c;就先怀疑下是不是文件损坏了 ls指令看不出任何端倪 文件名、后缀都正常 解压出现报错 瞅瞅文件大小 du -h <文件名> 呦呵 4kb&#xff0c;和应该的大…

Android studio Gradle下载失败,如何手动配置解决该问题详解

前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂&#xff0c;风趣幽默"&#xff0c;感觉非常有意思,忍不住分享一下给大家。 &#x1f449;点击跳转到教程 前言&#xff1a; 今天在打开公司一个项目时&#xff0c;突然要重新下载相关的gradle&am…

在表格中循环插入表单

<template><div class"key">{{ruleForm.casesRange}}<el-form label-position"top" :model"ruleForm" refruleForm><el-form-item label"这个表格怎么写"><el-table :data"tableData" border>…

【rk3229 android7.1.2 替换默认输入法】

问题平台描述 问题描述解决方法 郑重声明:本人原创博文&#xff0c;都是实战&#xff0c;均经过实际项目验证出货的 转载请标明出处:攻城狮2015 Platform: Rockchip CPU:rk3229 OS:Android 7.1.2 Kernel: 3.10 问题描述 国内客户&#xff0c;觉得安卓自带的输入法不好用&#x…

Ping工作原理

文章目录 目的ping网络协议 OSIICMP什么是ICMP作用功能报文类型查询报文类型差错报文类型ICMP 在 IPv4 和 IPv6 的封装ICMP 在 IPv4 协议中的封装ICMP 在 IPv6 协议中的封装ICMP 头部日常ping 排除步骤ping 查询报文使用code扩展目的 本文主要是梳理ping的工作原理- 揭开 ICMP…

项目实战-tpshop商城项目

项目实战-tpshop商城项目 环境部署准备软件工具准备远程连接测试远程连接测试-查看虚拟机IP地址远程连接测试-检测本机与虚拟机是否连通远程连接测试-通过远程工具连接linux服务器 常见问题处理 环境部署项目技术架构介绍部署tpshop项目-tpshop验证数据库验证用户信息表熟悉商品…

【C语言】内存函数

1. memcpy 使⽤和模拟实现 void * memcpy ( void * destination, const void * source, size_t num ); • 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。 • 这个函数在遇到 \0 的时候并不会停下来。 • 如果source和destination有任…

Java面试——Netty

优质博文&#xff1a;IT-BLOG-CN 一、BIO、NIO 和 AIO 【1】阻塞 IO(Blocking I/O)&#xff1a; 同步阻塞I/O模式&#xff0c;当一条线程执行 read() 或者 write() 方法时&#xff0c;这条线程会一直阻塞直到读取一些数据或者写出去的数据已经全部写出&#xff0c;在这期间这条…

数据库-第十一章 并发控制【期末复习|考研复习】

前言 总结整理不易&#xff0c;希望大家点赞收藏。 给大家整理了一下数据库系统概论中的重点概念&#xff0c;以供大家期末复习和考研复习的时候使用。 参考资料是王珊老师和萨师煊老师的数据库系统概论(第五版)。 数据库系统概论系列文章传送门&#xff1a; 第一章 绪论 第二/…

使用npm版本管理工具解决npm 的EACCES permissions errors when installing packages globally错误

EACCES错误通常表示“权限被拒绝”&#xff0c;意味着您没有足够的权限来执行某个操作。在计算机领域&#xff0c;尤其是在文件系统和程序安装中&#xff0c;这个错误很常见。以下是可能导致EACCES错误的原因以及相应的解决方法&#xff1a; 文件系统权限&#xff1a;当您尝试…

C++的学习

代码练习 输入一个字符串&#xff0c;统计其中大写字母、小写字母、数字、空格以及其他字符的个数 #include <iostream>using namespace std;int main() {cout << "请输入一个字符串" << endl;string str;getline(cin,str);int capital 0;int l…

快速搭建Vue前端框架

快速搭建Vue前端框架 安装Vue Vue官方安装过程:https://cli.vuejs.org/zh/guide/installation.html 二.创建Vue工程 2.2 安装淘宝镜像 安装淘宝镜像&#xff08;会让你安装Vue的速度加快&#xff09;&#xff1a; npm config set registry https://registry.npm.taobao.or…

Redis常见数据类型下

目录 Hash 哈希 常用指令 HSET HGET HEXISTS HDEL HKEYS HVALS HGETALL HMGET 内部编码 Hash类型和关系型数据库 缓存方式对比 List 列表 特点 常用命令 LPUSH LPUSHX RPUSH RPUSHX LRANGE LPOP / RPOP LINDEX LINSERT 阻塞(BLOCK)版…

ping多个IP的工具

Ping Tool 项目地址 python开发的IP搜索小工具 ping一个网段所有IP&#xff0c;显示结果查看某个ip地址开放监听的端口配置可保存

ArcGIS学习(十四)OD分析

ArcGIS学习(十四)OD分析 1.上海市KFC与麦当劳的空间聚集度分析 本任务给大家带来的内容是网络节点关系分析。网络节点关系分析一般也叫OD分析。“O”指的是起点(ORIGIN),"D”指的是终点(DESTINATION),0D分析即为基于起点到终点的分析。 网络节点关系分析我们经常…

java上传本地文件到服务器共享

在Windows系统中,将本地文件夹中的某个文件上传到另一台Windows服务器电脑上,前提:两台电脑网络互通,要接收文件的Windows服务器文件夹开启了共享,可以被本机用如下方式进行写入和读取: 如何配置服务器共享请自行百度查找。 所需要的maven依赖如下: <dependency>…

海格里斯HEGERLS智能托盘四向车系统为物流仓储自动化升级提供新答案

随着实体企业面临需求多样化、订单履行实时化、商业模式加速迭代等挑战&#xff0c;客户对物流仓储解决方案的需求也逐渐趋向于柔性化、智能化。作为近十年来发展起来的新型智能仓储设备&#xff0c;四向车系统正是弥补了先前托盘搬运领域柔性解决方案的空白。随着小车本体设计…