php eayswoole node axios crypto-js 实现大文件分片上传复盘
不啰嗦 直接上步骤
步骤1.开发环境配置
项目需要node.js 做前端支撑 官网下载地址:
http://nodejs.cn/download/
根据自己需要下载对应的版本,我下载的是windows系统64位的版本。
包下载好后 进行安装,安装步骤在此省略...
测试是否安装成功
如果是window 按住键盘Win+R 输入cmd 在终端里面输入
node -vnpm-v
如果安装成功会出现安装的node npm 的软件版本号,否则为安装失败。如下图

因为一些原因 npm 下载包巨慢 你懂得,所以我们这里选用淘宝镜像 打开终端 运行如下命令:
npm install -g cnpm --registry=https://registry.npm.taobao.org
安装好后 运行命令:
cnpm -v
cnpm@9.2.0 (C:\Users\King\AppData\Roaming\npm\node_modules\cnpm\lib\parse_argv.js)
npm@9.8.1 (C:\Users\King\AppData\Roaming\npm\node_modules\cnpm\node_modules\npm\index.js)
node@18.17.0 (D:\Program Files\node\node.exe)
npminstall@7.11.1 (C:\Users\King\AppData\Roaming\npm\node_modules\cnpm\node_modules\npminstall\lib\index.js)
prefix=C:\Users\King\AppData\Roaming\npm
win32 x64 10.0.22621
registry=https://registry.npmmirror.com
步骤2.构想分片上传逻辑,编写逻辑代码(核心)
基本思路:
1)前端侧 :前端上传文件,根据分片大小,自动计算出整个文件的分片数量,以及分片二进制文件,以及整个文件的md5值,以及分片文件md5值,传与后端,后端处理完后,根据上传分片的进度以及后端返回状态,判断整个文件是否传输完毕,完毕后,前端展示完成进度。结束整个分片上传逻辑。
2)后端PHP侧:后端接收前端传过来的数据,包括文件名,文件md5,分片信息,然后将分片文件信息存储到redis 有序集合中,其中key为整个文件的md5 ,待所有分片文件都上传完后,根据顺序,然后将文件整合存储,然后完成整个文件分片上传逻辑。
下面我们来编写相关代码 :
前置条件 我们已经安装了此环境 环境如下:
| 运行环境 | 版本 |
| Linux | centos 7.7.1908 |
| PHP | 7.4.19 |
| redis | 6.2.1 |
| swoole扩展 | 4.8.13 |
| eayswoole | 3.5.1 |
首先我们需要使用到redis 部分代码如下:
首先配置redis 相关信息 此处我们将配置文件放在根目录下Config 目录 Redis.php中 代码如下:
[root@web1 easyswoole]# cd Config
ll
[root@web1 Config]# ll
总用量 28
-rw-r--r-- 1 root root 8725 9月 23 19:09 Common.php
-rw-r--r-- 1 root root 1450 9月 4 21:21 Iam.php
-rw-r--r-- 1 root root 3027 8月 29 18:47 Mimes.php
-rw-r--r-- 1 root root 1795 9月 4 19:21 Mysql.php
-rw-r--r-- 1 root root 948 9月 23 17:50 Redis.php
[root@web1 Config]# vim Redis.php<?php
return ['redis' => [# 默认redis 配置'REDIS' => ['host' => '127.0.0.1','port' => '6390','auth' => '123456','db' => '1','serialize' => 0],# token存储redis,用来设置接口权限'REDIS_LOCAL' => ['host' => '127.0.0.1','port' => '6390','auth' => '123456','db' => 5,'serialize' => 0],]
];
配置上传目录 后续Upload.php 控制器需要读取 Config/Common.php 代码如下:
<?php
use EasySwoole\EasySwoole\Config;
defined('BASEPATH') or define('BASEPATH', dirname(__FILE__) . '/../..');
defined('WEB_IP') or define('WEB_IP', '192.168.1.1');
return [// 此处省略其他配置信息 .....'WEB_IP' => WEB_IP,'WEB_PATH' => BASEPATH,'UPLOAD' => ['tmp_dir' => '/uploads_tmp/',//分片文件缓存目录'upload_dir' => '/uploads/',//文件现在目录],// 此处省略其他配置信息 .....
];
接下来需要定义连接池 直接上代码
cd App/Pool
touch RedisPool.php
<?php
/*** redis连接池配置处理*/namespace App\Pool;use EasySwoole\Pool\Config;
use EasySwoole\Redis\Config\RedisConfig;
use EasySwoole\Redis\Redis;class RedisPool extends \EasySwoole\Pool\AbstractPool {protected $redis_config;public function __construct(Config $conf, RedisConfig $redis_config) {parent::__construct($conf);$this->redis_config = $redis_config;}protected function createObject() {return new Redis($this->redis_config);}
}
接下来,在入口文件EasySwooleEvent.php 注册redis 连接池
<?phpnamespace EasySwoole\EasySwoole;
use App\Pool\RedisPool;
use EasySwoole\Redis\Config\RedisConfig;class EasySwooleEvent implements Event {public static function mainServerCreate(EventRegister $register) {//其他逻辑 此处省略....//注册redis self::initRedis();//连接池热启动$register->add($register::onWorkerStart, function (\swoole_server $server, int $workerId) {if ($server->taskworker == false) {//每个worker进程都预创建连接$redis_arr = Config::getInstance()->getConf('redis');foreach ($redis_arr as $redis_name => $redis_conf) {\EasySwoole\Pool\Manager::getInstance()->get(strtolower($redis_name))->keepMin(10);//print_r(\EasySwoole\Pool\Manager::getInstance()->get(strtolower($redis_name))->status());}}});//其他逻辑 此处省略....}/*** 注册redis连接池*/public static function initRedis() {// 注册redis连接池$redis_arr = Config::getInstance()->getConf('redis');foreach ($redis_arr as $redis_name => $conf) {$config = new \EasySwoole\Pool\Config();$config->setMinObjectNum(8);$config->setMaxObjectNum(200);$config->setGetObjectTimeout(3.0);$redis_config = new RedisConfig($conf);//注册连接池管理对象\EasySwoole\Pool\Manager::getInstance()->register(new RedisPool($config, $redis_config), strtolower($redis_name));}}
}
接下来 新增相关路由信息
<?php
/** 路由*/
namespace App\HttpController;use EasySwoole\EasySwoole\Config;
use EasySwoole\Http\AbstractInterface\AbstractRouter;
use EasySwoole\Http\Request;
use EasySwoole\Http\Response;
use FastRoute\RouteCollector;class Router extends AbstractRouter
{function initialize(RouteCollector $routeCollector){$routeCollector->addGroup('/api/common', function (RouteCollector $router) {$router->post('/upload_file', '/Api/Upload/uploadFile'); //分片上传文件$router->post('/slice_upload_check', '/Api/Upload/checkFile'); //分片上传文件检测});}
}
Upload.php 相关控制器 代码如下:
<?php
/*** 文件上传(支持分片上传)*/namespace App\HttpController\Api;
use EasySwoole\Http\AbstractInterface\Controller;
use EasySwoole\EasySwoole\Config;
use EasySwoole\EasySwooleEvent;
use EasySwoole\RedisPool\Redis;
use EasySwoole\Http\Message\Stream;class Upload extends Controller
{/*** Notes: 存储文件到本地*/public function saveFileToLocalAction(){// $request = $this->request()->getRequestParam();$file = $this->request()->getUploadedFile('file');//上传的文件if (!$file) {return $this->returnMsg( [],50000,'上传出错请重试,请上传文件');}$tmp_file_name = $file->getClientFilename();$conf = Config::getInstance()->getConf();$dir = $conf["WEB_PATH"] . $conf['UPLOAD']['upload_dir'];if (!file_exists($dir)) {mkdir($dir, 0777);}$file_ext = uniqid();$suf_exp_arr = explode(".", $tmp_file_name);$file_name = $suf_exp_arr[0];#$move_to = $dir.$tmp_file_name;$move_to = $dir . $file_name . '_' . $file_ext . '.' . $suf_exp_arr[count($suf_exp_arr) - 1];if (file_exists($move_to)) {return $this->returnMsg( [],1,'已上传同名文件,请修改后再上传!');}if (!move_uploaded_file($file->getTempName(), $move_to)) {return $this->returnMsg( [],1,'上传失败,请稍后再试!');}$file_url = "http://" . $conf['WEB_IP'] . $conf['UPLOAD']['upload_dir'] . $file_name . '_' . $file_ext . '.' . $suf_exp_arr[count($suf_exp_arr) - 1];$return['file_url'] = $file_url;$return['img_url'] = $file_url;$return['file_name'] = $file_name . '_' . $file_ext . '.' . $suf_exp_arr[count($suf_exp_arr) - 1];return $this->returnMsg($return,0, "success");}/**** 文件检查* @return bool*/public function checkFile(){$request = $this->request()->getRequestParam();$suf_exp_arr = explode(".", $request['file_name']);$suf = $suf_exp_arr[count($suf_exp_arr) - 1];$can_upload_arr = ['zip','3G2','3GP','3GP2','3GPP','AMV','ASF','AVI','BIK','DIVX','DRC','DV','DVR-MS','EVO','F4V','FLV','GVI','GXF','M1V','M2T','M2TS','M2V','M4V','MKV','MOV','MP2V','MP4','MP4V','MPA','MPEG','MPEG1','MPEG2','MPEG4','MPG','MPV2','MTS','MTV','MXF','NSV','NUV','REC','RM','RMVB','RPL','THP','TP','TS','TTS','VOB','VRO','WMV','WTV','XESC','XMP','OGG','SWF','WEBM','GIF','264','601','692','800','801','av','avx','dat','dav','djl','dvr','g64','h3crd','h64','h264','jfv','jmv','kyd','lvf','mpk','nsf','nv4','ps','sdv','sv5','tm4',];if (!in_array(strtoupper($suf), $can_upload_arr) && !in_array(strtolower($suf), $can_upload_arr)) {return $this->returnMsg([], 30000, '请上传正确格式的文件');}//判断是否包含特殊字符if (strpos($suf_exp_arr[0], ',') !== false) {return $this->returnMsg([], 30000, '文件名不能包含英文逗号');}if (strpos($suf_exp_arr[0], ',') !== false) {return $this->returnMsg([], 30000, '文件名不能包含中文逗号');}$redis_key = $request['file_md5'] ?? '';$file_chunk_md5 = $request['file_chunk_md5'] ?? '';$status = \EasySwoole\Pool\Manager::getInstance()->get('redis')->invoke(function (\EasySwoole\Redis\Redis $redis) use ($redis_key, $file_chunk_md5) {$all_files = $redis->zRange($redis_key, 0, -1);if (in_array($file_chunk_md5, $all_files)) {$status = 1;} else {$status = 0;}return $status;});return $this->returnMsg([], $status);}/**** 文件上传*/public function uploadFile(){$request = $this->request()->getRequestParam();$all_chunk = $request['chunks'];//总分片数$now_chunk = $request['cur_chunk'];//当前分片//$original_filename = $request['original_filename']; //原始文件名$file = $this->request()->getUploadedFile('file_chunk');//上传的文件if (!$file) {$json = ['status' => 1,'message' => '上传出错请重试'];$this->response()->write(json_encode($json));return null;}$conf = Config::getInstance()->getConf();$dir = $conf["WEB_PATH"] . $conf['UPLOAD']['upload_dir'];$tmp_dir = $conf["WEB_PATH"] . $conf['UPLOAD']['tmp_dir'];//分片数据暂存文件夹if (!file_exists($dir)) {mkdir($dir, 0777);}if (!file_exists($tmp_dir)) {mkdir($tmp_dir, 0777);}$suf_exp_arr = explode(".", $request['file_name']);$suf = $suf_exp_arr[count($suf_exp_arr) - 1];if (move_uploaded_file($file->getTempName(), $tmp_dir . $request['file_chunk_md5'])) {//使用redis的有序集合存储文件名称用于合并$redis_key = $request['file_md5'];$file_status = \EasySwoole\Pool\Manager::getInstance()->get('redis')->invoke(function (\EasySwoole\Redis\Redis $redis) use ($redis_key, $request, $tmp_dir, $dir, $now_chunk, $all_chunk, $suf, $suf_exp_arr) {$redis->expire($redis_key, 7200); //2小时后过期$redis->zAdd($redis_key, $request['cur_chunk'] + 1, $tmp_dir . $request['file_chunk_md5']);if ($now_chunk == $all_chunk) {//文件合并$all_files = $redis->zRange($redis_key, 0, -1);if ($all_files && is_array($all_files)) {//创建要合并的最终文件资源$final_file = $dir . $request['file_md5'] . '.' . $suf;$final_file_handler = fopen($final_file, 'wb');foreach ($all_files as $k => $v) {$frag_file_handler = fopen($v, 'rb');$frag_file_content = fread($frag_file_handler, filesize($v));fwrite($final_file_handler, $frag_file_content);unset($frag_file_content);fclose($frag_file_handler); //关闭分片文件资源unlink($v); //删除已经合并的分片文件}$redis->zRemRangeByRank($redis_key, 0, -1);$save_path = $dir . "/" . date('Ymd', time());if (!file_exists($save_path)) {mkdir($save_path, 0777);}$new_file = $save_path . '/' . $request['file_md5'] . '.' . $suf;$status = rename($final_file, $new_file);return 'end';}} else {return 'ing';}});if (!in_array($file_status, ['end', 'ing'])) {$json = ['status' => 1,'message' => '上传出错请重试,重命名失败'];} else {$json = ['status' => 0,'message' => 'success','time' => time(),//'file_url' => "http://" . $conf["WEB_IP"] . $conf['UPLOAD']['upload_dir'] . $request['file_md5'] . '.' . $suf,//文件链接,'file_url' => "http://" . $conf["WEB_IP"] . $conf['UPLOAD']['upload_dir'] . '/' . date('Ymd', time()) . '/' . $request['file_md5'] . '.' . $suf,//文件链接,'data' => [],'file_status' => $file_status,];}} else {$json = ['status' => 1,'message' => '上传出错请重试'];}$this->response()->write(json_encode($json));}/*** @name: 返回值处理* @msg:* @param {array} $data* @param {int} $status* @param {string} $message* @param {array} $other* @param {int} $statusCode* @return {*}*/public function returnMsg(array $data = [], int $status = 0, string $message = 'success', array $other = [], int $statusCode = 200){$return = ['status' => $status,'message' => $message,'data' => $data,]; if ($other) {foreach ($other as $k => $v) {$return[$k] = $v;}}$this->response()->withHeader('Content-type', 'application/json;charset=utf-8')->withStatus($statusCode)->write(json_encode($return));$this->response()->end();return false;}}
步骤3.后端测试好后,我们需要编写前端页面
前面已经说过 我们需要node npm 前端环境,如果已经安装好了 请忽略
1)我们在任意一个目录下 打开终端cmd 然后运行命令 安装vue 脚手架:
npm install -g @vue/cli
2)创建一个新的Vue.js项目:
npm create vue@latest
一路按一下回车键
如下图:
3)进入项目目录:
进入创建的Vue.js项目目录:
cd vue-project
运行
npm install
4)安装axios 和 crypto-js
cnpm install axios
cnpm install crypto-js
5) 创建vue 实例
在Vue项目的入口文件中(通常是 src/main.js),创建Vue实例并将Vue组件添加到实例中。如下图:

6)实现上传
在Vue项目的入口文件中 src/App.vue 编写如下代码:
<template><div><input type="file" ref="fileInput" @change="handleFileChange" /><button @click="uploadFile">上传</button><div v-if="uploadProgress > 0 && !uploadComplete">上传进度: {{ uploadProgress }}%</div><div v-if="uploadComplete">上传完成</div></div>
</template><script>
import axios from "axios";
import CryptoJS from "crypto-js";export default {data() {return {file: null,chunkSize: 1024 * 1024, // 分片大小(1MB)currentChunk: 1, // 当前分片totalChunks: 0, // 总分片数fileMD5: "", // 文件的MD5值uploadProgress: 0, // 上传进度uploadComplete: false, // 上传是否完成};},methods: {handleFileChange(event) {// 重置上传状态this.uploadProgress = 0;this.uploadComplete = false;this.fileMD5 = "";this.file = event.target.files[0];this.totalChunks = Math.ceil(this.file.size / this.chunkSize);// 计算整个文件的MD5值const fileReader = new FileReader();fileReader.onload = () => {const fileData = fileReader.result;const wordArray = CryptoJS.lib.WordArray.create(fileData);this.fileMD5 = CryptoJS.MD5(wordArray).toString(CryptoJS.enc.Hex);console.log(this.fileMD5);};fileReader.readAsArrayBuffer(this.file);},async uploadFile() {if (!this.fileMD5) {console.error("文件MD5值为空");return;}// 并发处理每个分片文件const promises = [];for (let i = 1; i <= this.totalChunks; i++) {const chunkMD5 = await this.calculateChunkMD5(i);// 发起检查分片状态的请求const checkFormData = new FormData();checkFormData.append("file_name", this.file.name);checkFormData.append("file_md5", this.fileMD5);checkFormData.append("file_chunk_md5", chunkMD5);checkFormData.append("chunks", this.totalChunks);checkFormData.append("cur_chunk", i);promises.push(axios.post("/api/SPAP1/api/common/slice_upload_check", checkFormData).then((checkResponse) => {if (checkResponse.data.status !== 0) {alert(checkResponse.data.message);console.error("分片状态检查失败,请上传正确格式的文件");throw new Error("分片状态检查失败");}// 发起分片上传请求const startByte = (i - 1) * this.chunkSize;const endByte = Math.min(i * this.chunkSize, this.file.size);const chunk = this.file.slice(startByte, endByte);const uploadFormData = new FormData();uploadFormData.append("file_name", this.file.name);uploadFormData.append("file_md5", this.fileMD5);uploadFormData.append("file_chunk_md5", chunkMD5);uploadFormData.append("chunks", this.totalChunks);uploadFormData.append("cur_chunk", i);uploadFormData.append("file_chunk", chunk);return axios.post("/api/SPAP1/api/common/upload_file", uploadFormData, {onUploadProgress: (progressEvent) => {// 计算并更新上传进度const chunkUploaded = Math.round((progressEvent.loaded / progressEvent.total) * 100);this.uploadProgress = ((i - 1) / this.totalChunks) * 100 + (chunkUploaded / this.totalChunks);},}).then((uploadResponse) => {// 检查上传请求的响应if (uploadResponse.data.status !== 0) {alert(uploadResponse.data.message);console.error("上传请求失败,请上传正确格式的文件");throw new Error("上传请求失败");}// 如果文件状态为 "end",标记上传完成if (uploadResponse.data.file_status == "end") {this.uploadComplete = true;}});}));}try {await Promise.all(promises);if (this.uploadComplete) {alert("上传完成");console.log("上传完成");}} catch (error) {console.error("上传失败", error);}},calculateChunkMD5(chunkNumber) {return new Promise((resolve) => {const startByte = (chunkNumber - 1) * this.chunkSize;const endByte = Math.min(chunkNumber * this.chunkSize, this.file.size);const chunk = this.file.slice(startByte, endByte);const reader = new FileReader();reader.onload = (e) => {const arrayBuffer = e.target.result;const wordArray = CryptoJS.lib.WordArray.create(arrayBuffer);const md5 = CryptoJS.MD5(wordArray).toString(CryptoJS.enc.Hex);resolve(md5);};reader.readAsArrayBuffer(chunk);});},},
};
</script>
7)考虑到上面的axios 发送接口会与前端报跨域报错 故此这里采用axios 代理模式 进行处理

怎么解决跨域呢
在最外面的vite.config.js 文件中,加入server 这个参数
import { fileURLToPath, URL } from 'node:url'import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'// https://vitejs.dev/config/
export default defineConfig({plugins: [vue(),],resolve: {alias: {'@': fileURLToPath(new URL('./src', import.meta.url))}},server: {hmr: { overlay: false }, // 禁用或配置 HMR 连接 设置 server.hmr.overlay 为 false 可以禁用服务器错误遮罩层// 服务配置port: 8080, // 类型: number 指定服务器端口;open: false, // 类型: boolean | string在服务器启动时自动在浏览器中打开应用程序;cors: true, // 类型: boolean | CorsOptions 为开发服务器配置 CORS。默认启用并允许任何源proxy: {'/api': {target: 'http://192.168.31.128:86', // Your backend server URLchangeOrigin: true,pathRewrite: {'^/api': '', // Remove the '/api' prefix when forwarding the request},},}}
})
其中port 为前端页面端口 target 为后端接口地址 其他可以不变。
8)调试运行
在命令行运行调试命令:
npm run dev
9) 打开页面

上传一个正常的文件





Nice 基本上整个文件分片上传就完成了
相关文章:
php eayswoole node axios crypto-js 实现大文件分片上传复盘
不啰嗦 直接上步骤 步骤1.开发环境配置 项目需要node.js 做前端支撑 官网下载地址: http://nodejs.cn/download/ 根据自己需要下载对应的版本,我下载的是windows系统64位的版本。 包下载好后 进行安装,安装步骤在此省略... 测试是否安装成功 …...
《Upload-Labs》01. Pass 1~13
Upload-Labs 索引前言Pass-01题解 Pass-02题解总结 Pass-03题解总结 Pass-04题解 Pass-05题解总结 Pass-06题解总结 Pass-07题解总结 Pass-08题解总结 Pass-09题解 Pass-10题解 Pass-11题解 Pass-12题解总结 Pass-13题解 靶场部署在 VMware - Win7。 靶场地址:https…...
v-for中的key
在Vue中,当使用v-for指令循环渲染元素时,添加:key是一个推荐做法,尤其是在循环的元素可能会被重新排序、添加或删除的情况下。 :key的作用是为每个循环的元素提供一个唯一的标识符,以便Vue能够跟踪和管理这些元素的状态。Vue使用…...
MySQL学习笔记17
MySQL权限管理grant: 权限说明: Table 6.2 Permissible Privileges for GRANT and REVOKE PrivilegeGrant Table ColumnContextALL [PRIVILEGES]Synonym for “all privileges”Server administrationALTERAlter_privTablesALTER ROUTINEAlter_routin…...
跨境电商建站:选择域名需要注意什么?
在跨境电商建站过程中,选择一个合适的域名至关重要,尤其是对于跨境电商独立站来说,它对未来的seo排名和品牌建设都有着重要影响。关于本文,我会先从域名的定义开始,到域名选择的重要性,再到如何选择一个完美…...
jupyterlab
1. 环境:linux 环境(基于ubuntu-fork 镜像实现) 2. pip install jupyter notebook 3. 编译jupyterlab源代码必须使用 node 14.21.3 高版本编译报错#下载 node 14.21.3 :wget https://nodejs.org/download/release/latest-v14.x/node-v14.21.…...
Oracle的递归公共表表达式
查询节点id为2的所有子节点的数据,包括向下级联 WITH T1 (id, parent_id, data) AS (SELECT id, parent_id, dataFROM nodesWHERE id 2UNION ALLSELECT t.id, t.parent_id, t.dataFROM nodes tJOIN T1 n ON t.parent_id n.id ) SELECT * FROM T1; --建表语句 C…...
解决antd vue ts v-model:value绑定Boolean布尔类型爆红但可以使用
没啥好写的,写点注意点把 前言:在 antd vue中需要用到对应的类型转换,v-model后面补一个value来表明类型,但这也是默认类型,并不是指定类型默认是 (property) value?: string | number | undefined 字符,…...
zblog插件大全-zblog免费插件
在当今数字化时代,拥有一个精彩的博客或网站已经成为许多人追求的目标。通过博客,我们可以分享知识、表达观点,甚至创造收入。然而,维持一个充满新鲜内容的博客却不是一件容易的事情。 ZBlog自动采集插件 什么是ZBlog自动采集插件…...
思科、华为、华三、锐捷网络设备巡检命令
下面为四种设备巡检命令,以便日常查阅: 华三 screen-length disable 取消分页 displayversion 查看版本 display clock 查看日期时钟 display fan 查看风扇状态 display power 查看电源信息 display cpu-usage 查看CPU利用率 display memory 查看…...
正则表达式贪婪模式和非贪婪模式
一、贪婪模式 贪婪模式表示尽可能多的匹配字符串,正则表达式六个量词元字符?、、*、{n}、{n,m}、{n,}默认是贪婪模式 接下来引入一个场景来分析说明 获取html a标签href属性的值 <a href"https://www.baidu.com/" attr"abc"></a>…...
借助 ControlNet 生成艺术二维码 – 基于 Stable Diffusion 的 AI 绘画方案
背景介绍 在过去的数月中,亚马逊云科技已经推出了多篇博文,来介绍如何在亚马逊云科技上部署 Stable Diffusion,或是如何结合 Amazon SageMaker 与 Stable Diffusion 进行模型训练和推理任务。 为了帮助客户快速、安全地在亚马逊云科技上构建、…...
Codeforces Round 892 (Div. 2) - E. Maximum Monogonosity 思维dp 详细解析
题目链接 好久没有写题了复健一下qwq 题目大意 解题思路 这题目还挺妙的 首先考虑比较正常的dp, d p [ i ] [ j ] dp[i][j] dp[i][j] 为前 i i i的长度选 j j j个长度的最大价值,那么转移方程是: 图片来自:图片来源 但是这个是 …...
R语言中的数据重塑
文章目录 介绍reshape2::melt()的用法实例 reshape2::dcast()的用法实例 tidyr::gather()的用法tidyr::spread()的用法 介绍 tidyverse系列包中的函数操作都是针对简洁数据框进行的,对于不是简洁的数据,实现需要进行数据重塑。数据重塑主要包括长宽表的…...
基于Java实现的社区团购系统设计与实现(源码+lw+部署文档+讲解等)
文章目录 前言系统功能具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序(小蔡coding)有保障的售后福利 代码参考源码获取 前言 💗博主介绍:✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域…...
nodejs+vue网上婚纱购物系统elementui
便了用户足不出门也能进行购物的理念,方便了婚纱影楼的对商品的进一步管理,互联网成为人们快速获取、发布、和传递信息的重要渠道,它在人们政治、经济、生活等各个方面发挥着重要的作用。未来的时代是网络信息的时代,“网上生活方式”是人类今…...
【2023集创赛】加速科技杯三等奖作品:私密性高精度刷手身份认证系统
本文为2023年第七届全国大学生集成电路创新创业大赛(“集创赛”)加速科技杯三等奖作品分享,参加极术社区的【有奖征集】分享你的2023集创赛作品,秀出作品风采,分享2023集创赛作品扩大影响力,更有丰富电子礼…...
1500*C. Kefa and Park(dfstree)
Kefa and Park - 洛谷 Problem - 580C - Codeforces Examples input 4 1 1 1 0 0 1 2 1 3 1 4 output 2 input 7 1 1 0 1 1 0 0 0 1 2 1 3 2 4 2 5 3 6 3 7 output 2 解析: dfs遍历,记录前一个结点权值是否为1,并且累计路径1的个数…...
【2023保研】双非上岸东南网安
个人情况 学校:henu 专业:信息安全 排名:1/66 英语:六级500 竞赛:蓝桥杯PB国一,ISCC国一,密码数学挑战赛国三,还有其他一些省级水奖 论文:一篇EI在投(三作通…...
Redis与Mybatis
作者在学习Redis整合时使用JDBC与Jedis,但是呢,现如今的环境下,Mybatis系列ORM框架是更受关注的方法,作者有一点点Mybatis基础,Mybatisplus几乎忘的差不多了,现对Redis整合Mybatis相关知识进行梳理…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
