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-Type
            const 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.body
    const 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镜像

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Django模型层(附带test环境)

Django模型层(附带test环境) 目录 Django模型层(附带test环境)开启测试环境数据的增加数据的删除修改数据查询数据查询所有数据去重查询排序查询统计剔除指定数据多表查询校验数据是否存在字段的筛选查询 开启测试环境 首先在app下找到tests.py文件并进入 MyDJ.settings要换成…

【QA-SYSTEMS】CANTATA-解决Jenkins中build Cantata报错

【更多软件使用问题请点击亿道电子官方网站查询】 1、 文档目标 解决Jenkins中build Cantata测试项目报找不到license server的错误。 2、 问题场景 在Jenkins中build Cantata测试项目&#xff0c;报错“Failed to figure out the license server correctly”。 3、软硬件环…

虚拟化相关面试题集锦(0)—— 引言

经常关注博主的朋友应该能够发现&#xff0c;我近期开始在虚拟化尤其是QEMU/KVM上下功夫。这是由于我个人非常看好这个方向&#xff0c;把它当作今后的学习和工作的战略目标&#xff0c;同时也是个人非常喜欢和感兴趣的课题。 笔者看好虚拟化的原因是当前云计算已经如日中天&a…

短视频矩阵系统----矩阵系统源码搭建(技术门槛?)

短视频矩阵是什么意思&#xff1f;短视频矩阵的含义可以理解为全方位的短视频账号&#xff0c;通过不同的账号实现全方位的品牌展示。实际上是指一个短视频账号&#xff0c;通过不同的链接实现品牌展示&#xff0c;在不同的粉丝流量账号中互相转发同一个品牌&#xff0c;在主账…

05 | 深入浅出索引(下)

在上一篇文章中&#xff0c;我和你介绍了 InnoDB 索引的数据结构模型&#xff0c;今天我们再继续聊聊跟 MySQL 索引有关的概念。 在开始这篇文章之前&#xff0c;我们先来看一下这个问题&#xff1a; 在下面这个表 T 中&#xff0c;如果我执行 select * from T where k betwe…

022—pandas 根据时间段转换为各小时的秒数

前言 本例中&#xff0c;有一些时间段数据&#xff0c;需要将这些时间段里的时间以小时为分组&#xff0c;将24个小时段中每个小时所占用的秒数计算出来。 需求&#xff1a; 以第一条数据为例&#xff0c;它所在两个小时&#xff0c;7点段占用24分钟15秒&#xff0c;8点段54…

大数据组件之Hadoop图文介绍

前言 在当今大数据时代&#xff0c;回顾技术发展历程&#xff0c;Hadoop作为一项具有里程碑意义的开源项目&#xff0c;在大数据存储和处理领域曾一度占据主导地位。诞生于2005年的Apache Hadoop&#xff0c;以其独特的分布式文件系统&#xff08;HDFS&#xff09;和高效的并行…

遗传算法理解与代码实战(一)- demo(python手写代码)

遗传算法&#xff08;Genetic Algorithm, GA&#xff09;是模拟自然界中生物进化的机制来搜索最优解的方法。遗传算法属于进化计算的一部分&#xff0c;它借鉴了达尔文的自然选择和孟德尔的遗传学原理。 1、算法背景 遗传算法的灵感来源于生物进化过程。在自然界中&#xff0…

string 的模拟实现

string 的相关介绍&#xff1a;C&#xff1a;string相关内容的简单介绍-CSDN博客 成员变量&#xff1a; private:char* _strsize_t _sizesize_t _capacity 构造函数 string类的构造函数不仅需要完成空间的开辟&#xff0c;还需要再开辟的过程中完成字符串的拷贝&#xff0c;它…

ThreadLocal在实际开发中如何使用?

在实际开发中&#xff0c;ThreadLocal 是一个非常有用的工具&#xff0c;用于解决多线程环境下数据隔离和线程上下文数据的问题。以下是一个关于 ThreadLocal 在实际开发中使用的详细讲解&#xff0c;包括其工作原理、应用场景和实战例子。 1. 工作原理 ThreadLocal 类…

Mybatis从入门到CRUD到分页到日志到Lombok到动态SQL再到缓存

Mybatis 入门 1.导入maven依赖 <dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>x.x.x</version> </dependency>2.配置核心文件 <?xml version"1.0" encoding"U…

FISCO BCOS区块链平台上的智能合约压力测试指南

引言 在当今的分布式系统中&#xff0c;区块链技术因其去中心化、安全性和透明性而备受关注。随着区块链应用的不断扩展&#xff0c;对其性能和稳定性的要求也越来越高。因此&#xff0c;对区块链网络进行压力测试显得尤为重要。 目录 引言 1. 配置FISCO BCOS节点 2. 安装和…

Linux内核源码分析(强烈推荐收藏!)

一&#xff0c;前言 Linux内核是一个操作系统&#xff08;OS&#xff09;内核&#xff0c;本质上定义为类Unix。它用于不同的操作系统&#xff0c;主要是以不同的Linux发行版的形式。Linux内核是第一个真正完整且突出的免费和开源软件示例。Linux 内核是第一个真正完整且突出的…

Mysql - is marked as crashed and should be repaired

概述 上周发生了一个Mysql报错的问题&#xff0c;今天有时间整理一下产生的原因和来龙去脉&#xff0c;Mysql的版本是5.5,发生错误的表存储引擎都是MyISAM,产生的报错信息是Table xxxxxx is marked as crashed and should be repaired。 定位问题 产生的后果是Nginx服务没有…

MT6771 android13 自定义背光曲线

一. Android系统源码中的参数配置 MTK6771平台自己重写了背光曲线的参数&#xff0c;路径在s0_vnd/vendor/mediatek/proprietary/packages/overlay/vendor/FrameworkResOverlayExt/brightness_adaptive_support/res/values/config.xml 不过MTK的其他平台可能不是在这个路径 来看…

Linux Ubuntu部署SVN服务端结合内网穿透实现客户端公网访问

文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…

WordPress建站入门教程:如何上传安装WordPress主题?

我们成功搭建WordPress网站后&#xff0c;默认使用的是自带的最新主题&#xff0c;但是这个是国外主题&#xff0c;可能会引用一些国外的资源文件&#xff0c;所以为了让我们的WordPress网站访问速度更快&#xff0c;强烈建议大家使用国产优秀的WordPress主题。 今天boke112百…

javascript中字符串处理,常用的方法汇总

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;前端泛海 景天的主页&#xff1a;景天科技苑 文章目录 字符串对象的的相关方法1.获取字符串长度 length2.通过索引获取元素 …

让娃学习效率更高的“可视化”时间管理器

如果要问&#xff0c;老母亲在娃开学后&#xff0c;蕞着急孩子哪一种坏习惯&#xff0c;那时间管理肯定榜上有名&#xff01; 做作业的时候&#xff0c;才写了5分钟&#xff0c;已经没有耐心了&#xff0c;东摸摸西看看&#xff0c;一会说肚子疼想上厕所&#xff0c;一会又拿出…

Linux中线程的实现,线程的接口相关函数pthread_create、pthread_join、pthread_exit

目录 一.线程的概念 二.操作系统中线程的实现 三.Linux中线程的实现 四.进程与线程的区别 五.线程的接口相关函数 5.1 pthread_create 5.2 pthread_join 5.3 pthread_exit 六.代码演示 七.如何解决上述问题&#xff1f; 方案1. 方案2. 方案3. 一.线程的概念 进程是…