uniapp使用Canvas给图片加水印把临时文件上传到服务器
生成的临时路径是没有完整的路径没办法上传到服务器
16:37:40.993 添加水印后的路径, _doc/uniapp_temp_1710923708347/canvas/17109238597881.png
16:37:41.041 添加水印后的完整路径, file://storage/emulated/0/Android/data/com.jingruan.zjd/apps/__UNI__BE4B000/doc/uniapp_temp_1710923708347/canvas/17109238597881.png
使用以下代码得到完整的路径
let path = 'file:/' + plus.io.convertLocalFileSystemURL(tempFilePath);
完整代码如下 使用的插件市场的hpy-watermark组件 一共2个
效果是
其他页面调用方式
<!--
增加水印上传villageReviewForm.preciseAddr 是通过高德获取的定位地址
-->
<hpy-watermark ref="uploadImage" :address="villageReviewForm.preciseAddr" @waterMark="waterMark"></hpy-watermark>
高德获取定位
uni.getLocation({type: 'gcj02',geocode: true,isHighAccuracy: true,success: res => { const {province,city,district,street,streetNum,poiName} = res.address;this.villageReviewForm.preciseAddr =`${district}${street}${streetNum}${poiName}${res.longitude},${res.latitude}`;console.log("经纬度地点",this.villageReviewForm.preciseAddr)// 数据渲染this.$forceUpdate();}});
获取上传数据结果
const fileList = this.$refs.uploadImage.fileList
组件样式
hpy-watermark.vue
<template><view><view class="watermark-content"><canvas canvas-id="watermarkCanvas" id="watermarkCanvas" :style="{width:canvasWidth + 'px', height:canvasHeight + 'px'}"></canvas></view><upload-image v-model="fileList" style="margin-left: 15rpx" :image-styles="imageStyles" :files-list="filesList" :delIcon="delIcon" @choose="chooseImage" @delFile="delFile"><slot><view class="is-add"><view class="icon-add"></view><view class="icon-add rotate"></view></view></slot></upload-image></view>
</template><script>import {fileServerIp} from "@/common/utils/config.js"import Session from "@/common/Session";import uploadImage from './upload-image.vue'export default {components: {uploadImage, },name:'hpy-watermark',props:{address:{type:String,default:''},delIcon: {type: Boolean,default: true},listStyles: {type: Object,default () {return {// 是否显示边框border: true,// 是否显示分隔线dividline: true,// 线条样式borderStyle: {}}}},imageStyles: {type: Object,default () {return {width: 'auto',height: 'auto'}}},/*** 文字文字位置(默认:左下角)可选值:左上角:topLeft、右上角:topRight、左下角:bottomLeft、右下角:bottomRight*/markAlign:{type:String,default:function(){return 'bottomLeft'}},/*** 设置文本的水平对齐方式,默认:start,文本在指定的位置开始。* end 文本在指定的位置结束。* center 文本的中心被放置在指定的位置。* left 文本左对齐。* right 文本右对齐。*/textAlign:{type:String,default:function(){return 'start';}},/*** 设置文本的垂直对齐方式,默认:alphabetic文本基线是普通的字母基线。* top 文本基线是 em 方框的顶端。* hanging 文本基线是悬挂基线。* middle 文本基线是 em 方框的正中。* ideographic 文本基线是表意基线。* bottom 文本基线是 em 方框的底端。*/textBaseline:{type:String,default:function(){return 'alphabetic';}},/*** 文字大小*/fontSize:{type:[Number, String],default:30},/*** 文字颜色*/fontColor:{type:String,default:function(){return 'red'}},/*** 阴影颜色*/shadowColor:{type:String,default:function(){return 'rgba(0, 0, 0, 1.0)';}},/*** 阴影边框大小*/shadowWidth:{type:[Number, String],default:2},/*** 图片的质量,取值范围为 (0, 1],不在范围内时当作1处理*/quality:{type:[Number, String],default:1},/*** 目标文件的类型,只支持 'jpg' 或 'png'。默认为 'png'*/fileType:{type:String,default:function(){return 'png'}}},data() {return {fileList: [],files: [],filesList:[],canvasWidth:0,canvasHeight:0};},watch: { fileList: {handler(newVal, oldVal) { this.filesList=newVal;},immediate: true},},methods: {// 选择图片chooseImage() {if(this.isEmpty(this.address)){uni.showToast({icon:'none',title:'请打开定位或者重新获取'});return;}uni.chooseImage({count: this.limit, // 限制的图片数量sizeType: ['compressed'], // original 原图,compressed 压缩图,默认二者都有 sourceType: [ 'camera'],// album 从相册选图,camera 使用相机,默认二者都有success: (res) => {var imgPathList = res.tempFilePaths;if(imgPathList.length > 0){this.addImages(imgPathList);}},fail: (err) => {console.log('chooseImage fail', err)if("chooseImage:fail cancel" == err.errMsg){uni.showToast({icon:'none',title:'取消了选择'});}else{}}});},// 添加图片addImages(filePaths){if(filePaths.length > 0){var fillTexts = ["地址:"+this.address];fillTexts.push("时间:" + this.getNowTime());// 添加水印this.addWaterMark({filePaths,fillTexts});}},/*** 水印添加回调,在H5平台下,filePath 为 base64*/waterMark(filePath){this.imageList.push(filePath);},/*** 获取当前时间*/getNowTime(){var date = new Date(),year = date.getFullYear(),month = date.getMonth() + 1,day = date.getDate(),hour = date.getHours() < 10 ? "0" + date.getHours() : date.getHours(),minute = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes(),second = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();month >= 1 && month <= 9 ? (month = "0" + month) : "";day >= 0 && day <= 9 ? (day = "0" + day) : "";return (year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second);},/*** 删除文件* @param {Object} index*/delFile(index) {this.$emit('delete', {tempFile: this.filesList[index],tempFilePath: this.filesList[index].url})this.filesList.splice(index, 1) },/*** 增加水印* @param {Object} {filePaths:['图片地址1', '图片地址2'], fillTexts:['水印1', '水印2']}*/async addWaterMark({ filePaths = [], fillTexts = [] }) {try{for (const filePath of filePaths) {await this.drawImage(filePath, fillTexts.reverse());}}catch(e){// TODO handle the exception}finally{// uni.hideLoading();}},/*** 绘制单个图片*/async drawImage(filePath, fillTexts){uni.showLoading({title:'图片处理中···'});const ctx = uni.createCanvasContext('watermarkCanvas', this);return new Promise(resolve => {uni.getImageInfo({src: filePath,success: (image) => {this.canvasWidth = image.width;this.canvasHeight = image.height;ctx.clearRect(0, 0, image.width, image.height);setTimeout(()=>{ctx.drawImage(image.path, 0, 0, image.width, image.height);ctx.setFontSize(this.fontSize);ctx.setFillStyle(this.fontColor);// 设置阴影let shadowWidth = Number(this.shadowWidth + "");if(shadowWidth > 0){ctx.shadowColor = this.shadowColor;ctx.shadowOffsetX = shadowWidth;ctx.shadowOffsetY = shadowWidth;}// 设置水平对齐方式ctx.textAlign = this.textAlign;// 设置垂直对齐方式ctx.textBaseline = this.textBaseline;const maxText = fillTexts.reduce((text, val) => {return text.length >= val.length ? text : val;});fillTexts.forEach((mark, index) => {if(this.markAlign == "bottomRight"){ctx.fillText(mark, image.width - (ctx.measureText(maxText).width+60), image.height - (index*60+60));}else if(this.markAlign == "topLeft"){ctx.fillText(mark, 20, (index*60+60));}else if(this.markAlign == "topRight"){ctx.fillText(mark, image.width - (ctx.measureText(maxText).width+60), (index*60+60));}else{ctx.fillText(mark, 20, image.height - (index*60+60));}});ctx.draw(false, (() => {setTimeout(()=>{uni.canvasToTempFilePath({canvasId: 'watermarkCanvas',fileType:this.fileType,quality:Number(this.quality + "" || "1"),success: (res) => { console.log("添加水印后的路径",res.tempFilePath )this.saveUploadImage(res.tempFilePath )},fail:(err) => {uni.hideLoading();console.log(err)},complete: () => {resolve();}}, this);}, 300);})());}, 200);},fail: (e) => {resolve();}});});},saveUploadImage(tempFilePath){uni.showLoading({title:'图片上传中···'});// #ifdef APP-PLUSvar p = plus.io.convertLocalFileSystemURL(tempFilePath);this.url = 'file:/' + pconsole.log("添加水印后的完整路径",this.url )// #endifuni.uploadFile({url: fileServerIp + 'common/upload',name: "file",// #ifdef H5filePath: tempFilePath,// #endif// #ifdef APP-PLUSfilePath: this.url,// #endifheader: {Authorization: "Bearer " + Session.getValue('token')},success: uploadFileRes => {uni.hideLoading();const {data} = JSON.parse(uploadFileRes.data)this.filesList.push({url: data.url,name: data.fileName,extname: 'png'}) this.$emit('waterMark',{url: data.url,name: data.fileName,extname: 'png'});},fail: error => {uni.hideLoading();uni.showToast({title: '上传失败!',icon: 'error',duration: 2000});}})}}}
</script><style scoped>.watermark-content{width: 0;height: 0;overflow: hidden;}.uni-file-picker__container {/* #ifndef APP-NVUE */display: flex;box-sizing: border-box;/* #endif */flex-wrap: wrap;margin: -5px;}.rotate {position: absolute;transform: rotate(90deg);}.icon-add {width: 50px;height: 5px;background-color: #f1f1f1;border-radius: 2px;}
</style>
upload-image.vue
<template><view class="uni-file-picker__container"><view class="file-picker__box" v-for="(item,index) in filesList" :key="index" :style="boxStyle"><view class="file-picker__box-content" :style="borderStyle"><image class="file-image" :src="item.url" mode="aspectFill" @click.stop="prviewImage(item,index)"></image><view v-if="delIcon && !readonly" class="icon-del-box" @click.stop="delFile(index)"><view class="icon-del"></view><view class="icon-del rotate"></view></view><view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress"><progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4":backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" /></view><view v-if="item.errMsg" class="file-picker__mask" @click.stop="uploadFiles(item,index)">点击重试</view></view></view><view v-if="filesList.length < limit && !readonly" class="file-picker__box" :style="boxStyle"><view class="file-picker__box-content is-add" :style="borderStyle" @click="choose"><slot><view class="icon-add"></view><view class="icon-add rotate"></view></slot></view></view></view>
</template><script>export default {name: "uploadImage",emits:['uploadFiles','choose','delFile'],props: {filesList: {type: Array,default () {return []}},disabled:{type: Boolean,default: false},disablePreview: {type: Boolean,default: false},limit: {type: [Number, String],default: 9},imageStyles: {type: Object,default () {return {width: 'auto',height: 'auto',border: {}}}},delIcon: {type: Boolean,default: true},readonly:{type:Boolean,default:false}},computed: {styles() {let styles = {width: 'auto',height: 'auto',border: {}}return Object.assign(styles, this.imageStyles)},boxStyle() {const {width = 'auto',height = 'auto'} = this.styleslet obj = {}if (height === 'auto') {if (width !== 'auto') {obj.height = this.value2px(width)obj['padding-top'] = 0} else {obj.height = 0}} else {obj.height = this.value2px(height)obj['padding-top'] = 0}if (width === 'auto') {if (height !== 'auto') {obj.width = this.value2px(height)} else {obj.width = '33.3%'}} else {obj.width = this.value2px(width)}let classles = ''for(let i in obj){classles+= `${i}:${obj[i]};`}return classles},borderStyle() {let {border} = this.styleslet obj = {}const widthDefaultValue = 1const radiusDefaultValue = 3if (typeof border === 'boolean') {obj.border = border ? '1px #eee solid' : 'none'} else {let width = (border && border.width) || widthDefaultValuewidth = this.value2px(width)let radius = (border && border.radius) || radiusDefaultValueradius = this.value2px(radius)obj = {'border-width': width,'border-style': (border && border.style) || 'solid','border-color': (border && border.color) || '#eee','border-radius': radius}}let classles = ''for(let i in obj){classles+= `${i}:${obj[i]};`}return classles}},methods: {uploadFiles(item, index) {this.$emit("uploadFiles", item)},choose() {this.$emit("choose")},delFile(index) {this.$emit('delFile', index)},prviewImage(img, index) {let urls = []if(Number(this.limit) === 1&&this.disablePreview&&!this.disabled){this.$emit("choose")}if(this.disablePreview) returnthis.filesList.forEach(i => {urls.push(i.url)})uni.previewImage({urls: urls,current: index});},value2px(value) {if (typeof value === 'number') {value += 'px'} else {if (value.indexOf('%') === -1) {value = value.indexOf('px') !== -1 ? value : value + 'px'}}return value}}}
</script><style lang="scss">.uni-file-picker__container {/* #ifndef APP-NVUE */display: flex;box-sizing: border-box;/* #endif */flex-wrap: wrap;margin: -5px;}.file-picker__box {position: relative;// flex: 0 0 33.3%;width: 33.3%;height: 0;padding-top: 33.33%;/* #ifndef APP-NVUE */box-sizing: border-box;/* #endif */}.file-picker__box-content {position: absolute;top: 0;right: 0;bottom: 0;left: 0;margin: 5px;border: 1px #eee solid;border-radius: 5px;overflow: hidden;}.file-picker__progress {position: absolute;bottom: 0;left: 0;right: 0;/* border: 1px red solid; */z-index: 2;}.file-picker__progress-item {width: 100%;}.file-picker__mask {/* #ifndef APP-NVUE */display: flex;/* #endif */justify-content: center;align-items: center;position: absolute;right: 0;top: 0;bottom: 0;left: 0;color: #fff;font-size: 12px;background-color: rgba(0, 0, 0, 0.4);}.file-image {width: 100%;height: 100%;}.is-add {/* #ifndef APP-NVUE */display: flex;/* #endif */align-items: center;justify-content: center;}.icon-add {width: 50px;height: 5px;background-color: #f1f1f1;border-radius: 2px;}.rotate {position: absolute;transform: rotate(90deg);}.icon-del-box {/* #ifndef APP-NVUE */display: flex;/* #endif */align-items: center;justify-content: center;position: absolute;top: 3px;right: 3px;height: 26px;width: 26px;border-radius: 50%;background-color: rgba(0, 0, 0, 0.5);z-index: 2;transform: rotate(-45deg);}.icon-del {width: 15px;height: 2px;background-color: #fff;border-radius: 2px;}
</style>
相关文章:

uniapp使用Canvas给图片加水印把临时文件上传到服务器
生成的临时路径是没有完整的路径没办法上传到服务器 16:37:40.993 添加水印后的路径, _doc/uniapp_temp_1710923708347/canvas/17109238597881.png 16:37:41.041 添加水印后的完整路径, file://storage/emulated/0/Android/data/com.jingruan.zjd/apps/__UNI__BE4B000/doc/…...

小希的迷宫
目录 描述 输入 输出 样例输入 样例~~输出~~ 思路 code 描述 Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走。但是她设计迷宫的思路不一样,首先她认为所有的通道都应该是双向连通的&…...

MySQL索引剖析【了解背后的数据结构】
文章目录 常用索引概念聚簇索引 🎉非聚簇索引(二级索引) 数据结构选择Hash结构 ⭐️有序数组二叉搜索树AVL树(平衡二叉搜索树)B-Tree(多路平衡查找树)BTree ⭐️ MySQL中索引的实现InnoDB 索引实…...

004——内存映射(基于鸿蒙和I.MAX6ULL)
目录 一、 ARM架构内存映射模型 1.1 页表项 1.2 一级页表映射过程 1.3 二级页表映射过程 1.4 cache 和 buffer 二、 鸿蒙内存映射代码学习 三、 为板子编写内存映射代码 3.1 内存地址范围 3.2 设备地址范围 一、 ARM架构内存映射模型 (以前我以为页表机制…...

150 Linux C++ 通讯架构实战6 服务器程序目录规划,makefile编写
从无到有产生这套 通讯架构源代码【项目/工程】 一,服务器程序目录规划 一个完整的项目 肯定会有多个源文件,头文件,会分别存放到多个目录; 我们这里要规划项目的目录结构; 注意:不固安是目录还是文件&am…...
OpenCV支持哪些类型的文件格式读写?
OpenCV支持多种类型的文件格式读写,包括但不限于以下格式: Windows位图文件:包括BMP和DIB格式。JPEG文件:支持JPEG、JPG和JPE三种扩展名。便携式网络图片:即PNG格式。便携式图像格式:包括PBM、PGM和PPM三种…...
数据库中使用IN操作效率问题
1. IN操作的基本概念 IN操作符在SQL中用于指定某个字段的值是否匹配列表中的任何值。这是一个条件操作符,用于在WHERE子句中过滤记录。 SQL语法示例: SELECT * FROM table_name WHERE column_name IN (value1, value2, ...); 2. IN操作的效率问题 当…...

unity学习(67)——控制器Joystick Pack方向
1.轮盘直接复制一个拖到右边就ok了,轮盘上是有脚本的。(只复制) 2.上面的显示窗也可以复制,但是要绑定对应的轮盘(unity中修改变量),显示窗上是有脚本的。(复制改变量) 3…...

MATLAB的使用(一)
一,MATLAB的编程特点 a,语法高度简化; b,脚本式解释型语言; c,针对矩阵的高性能运算; d,丰富的函数工具箱支持; e,通过matlab本体构建跨平台; 二,MATLAB的界面 工具栏:提供快捷操作编辑器…...

JMeter并发工具的使用
视频地址:Jmeter安装教程01_Jmeter之安装以及环境变量配置_哔哩哔哩_bilibili 一、JMeter是什么 JMeter是一款免安装包,官网下载好后直接解压缩并配置好环境变量就可以使用。 环境变量配置可参考:https://www.cnblogs.com/liulinghua90/p/…...

基于springboot+vue的毕业就业信息管理系统
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 主要内容:毕业设计(Javaweb项目|小程序|Pyt…...
有什么小程序适合个人开发?
在这个信息爆炸的时代,小程序已经成为了我们生活中的一部分。无论是出行、购物还是娱乐,小程序都能为我们提供便捷的服务。对于个人开发者来说,开发一个小程序不仅可以锻炼自己的技术能力,还可以为他人提供便利,甚至有…...

【ARXIV2402】MambaIR
这个工作首次将 Mamba 引入到图像修复任务,关于为什么 Mamba 可以用于图像修复,作者有非常详细的解释:一路向北:性能超越SwinIR!MambaIR: 基于Mamba的图像复原基准模型 作者认为Mamba可以理解为RNN和CNN的结合…...

【计算机网络篇】数据链路层(3)差错检测
文章目录 🥚误码🍔两种常见的检错技术⭐奇偶校验⭐循环冗余校验🎈例子 🥚误码 误码首先介绍误码的相关概念 🍔两种常见的检错技术 ⭐奇偶校验 奇校验是在待发送的数据后面添加1个校验位,使得添加该校验…...
软件配置管理计划
1. 配置管理目标 本软件配置管理计划的目标在于确保软件开发生命周期内的所有配置项(CI)都得到适当的标识、控制、版本管理和追踪。通过实施有效的配置管理,我们的目标是: 保持配置项的一致性和完整性。确保配置项的可追溯性。减…...
嵌入式备考错题汇总
若某条无条件转移汇编指令采用直接寻址,则该指令的功能是将指令中的地址码送入()。 A.PC(程序计数器) B.AR(地址寄存器) C.AC(累加器) D.ALU(算术逻辑运算单元) 解析:选A,直接寻址是指操作数存放在内存单元中,指令中直接给出操作数所在存储单…...

38 mars3d 对接地图图层 绘制点线面员
前言 这里主要是展示一下 mars3d 的一个基础的使用 主要是设计 接入地图服务器的 卫星地图, 普通的二维地图, 增加地区标记 基础绘制 点线面园 等等 测试用例 <template><div style"width: 1920px; height:1080px;"><div class"mars3dClas…...
什么是Webhook 和 HTTP Endpoint?
Webhook 和 HTTP Endpoint 都是基于HTTP协议的网络通信概念,但它们在使用场景和目的上有所不同。 Webhook Webhook 是一种允许一个应用程序提供实时信息给其他应用程序的方法,这种通信是基于HTTP的“回调”或“钩子”。Webhook 通常被用来在一种服务上…...

小程序跨端组件库 Mpx-cube-ui 开源:助力高效业务开发与主题定制
Mpx-cube-ui 是一款基于 Mpx 小程序框架的移动端基础组件库,一份源码可以跨端输出所有小程序平台及 Web,同时具备良好的拓展能力和可定制化的能力来帮助你快速构建 Mpx 应用项目。 Mpx-cube-ui 提供了灵活配置的主题定制能力,在组件设计开发阶…...

GDC期间LayaAir启动全球化战略
3 月 18 日至 3 月 22 日,一年一度的游戏开发者大会(GDC)在美国旧金山举行。在此期间,Layabox宣布LayaAir引擎启动全球扩张战略,这标志着引擎将步入快速发展的新阶段。此举旨在利用公司先进的3D引擎技术,将…...

超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...

P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...

高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...

《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...