使用 golang 以及 Gin 框架,将上传的图片在不保存至本地的情况下添加水印,并上传至阿里云 OSS

正如标题所述,使用golang对上传图片添加水印,以及将图片上传到阿里云OSS,网上一搜索,便有你想要的结果了,可是,他们却先将上传图片添加水印后保存在本地,而后再将添加了水印的图片上传到阿里云OSS

这无疑是暂时占用了你电脑的磁盘空间(这里说是暂时,因为你可以通过程序对上传到阿里云OSS成功的图片进行删除),即便是这样,它也是消耗了磁盘读写操作,虽然人类是察觉不到这么细微的变化。

然而,作为技术人员的我们(不介意我这么称呼自己吧!),使能够Review出这段程序的问题。很显然,本地图片是要通过程序处理,添加水印,而后最终储存到阿里云OSS中,而不是又要在本地多存了一张比原图多了些水印的图片。

图像处理库

既然我们的目的明确了,那么就卷起柚子加油干吧!

虽然,使用golang对上传图片添加水印,以及如何将图片上传到阿里云OSS中,是两个独立分开的。但是,这两步看似分而食之的程序,其实是可以合二为一的!(我想,正在浏览本文的你,也是这么想的!)

这是可行的方案,毕竟先前的秋码记录 就是这么做的(那是使用Java构建的那些年!当然现在的 秋码记录 改用 ```Hugo·``搭建了)。

既然,java能实现对上传图片添加水印,而不暂时保存在本地,立马即可上传到阿里云OSS中。想必,golang也是可以完成的!

然而,网上相关的资料有限,才能铸就本文的诞生!

首先,我是用golang的版本是1.19,虽然不是最新的,但却不妨碍我们继续对本文的讲解。

虽然,人类总是对新鲜事物充满好奇,从而萌生了猎奇心。但也只有在旧事物的烘衬下,人们才能对新事物寄以最大的希望!这就好比,老婆总是别人的好看(当然,别人的老婆也有没有自己老婆好看的),这才造就了黄脸婆这一全国男人在没有老婆在身旁统一叹息声!

你得在你电脑任一磁盘下,新建一空文件夹,随后打开黑窗口(Terminal),输入以下命令,说明是modules进行管理的(毕竟我使用的是golang 1.19):

go mod init qiucode.cn/uploadImage

对于以上这行命令,就是初始化golang模块(module),在golang的世界是```万物皆可模块``(我说的如果不对,那么说出你的想法)。

初始化项目后,我们会发现文件夹下多了个go.mod文件(这个就是用来管理第三方依赖库的管理文件,不需要操作!),在该同级目录下新建main.go文件。

既然本文是探讨如何实现图片添加水印的,那么引入图像库那是必不可少的!

go get github.com/fogleman/gg

本文不会对这个图像库进行深层次的讲解!毕竟本文的核心内容是介绍如何对图片添加水印,而不是避重就轻本末倒置讲起了本该一笔带过的东西,却花了浓墨重彩去着重的描绘刻画它,到头来,却失了初心,乱了本文的主旨,实属不该。(实在不清楚的,可以去查看其文档)

很显然,将图片上传到阿里云OSS上,引入其SDK,那是必不可少的(关于这一点,应该不需要我多费口舌了吧)

go get github.com/aliyun/aliyun-oss-go-sdk/oss

接下来的这个依赖库,并不是必须的(那就是可选的),毕竟,有它没有它,本文都可以实现!

go get github.com/gin-gonic/gin

是啊!都说了,不引入它,本文也可以实现,那么我为什么还引入了呢?(关于这一点,欢迎你能在评论中留下你的只言片语,请不要吝啬你的文字!)

用代码实现标题的需求

前提准备工作就绪,我们该正式进入编码环节了。到了这里,正处于屏幕前的你,想必早就迫不及待了吧!毕竟,你就是与我有同样的需求,才会浏览本文的(当然也有那种,鼠标不小心点到了,但那种几率是很小的,我相信你不是属于那一类的)

首先,我们使用VS Code(你也可以使用其他你平时常用的IDE,不必非要使用与我一样的VS Code,然而,作为一款开源免费的IDE,你还有什么理由不去使用它呢?哦,差点忘记了,都说了,使用IDE是个人的自由,可我这么一说,倒有了让你使用VS Code之嫌。虽然,或许你比较喜欢使用收费软件,但你都是找的破解方法来破解软件,从而使用它们)

打开刚刚新建的main.go文件,开始逐步实现标题的需求。

package mainimport ("bytes""image""image/png" // "image/jepg"  //用于对 jpg 格式的图片进行处理 本文暂时不对 jpg 图片做处理"github.com/gin-gonic/gin"  //web 框架"github.com/fogleman/gg"  // 图像处理哭"github.com/aliyun/aliyun-oss-go-sdk/oss" //阿里云OSS SDK
)

随后,我们在main函数中实现标题中的需求。

func main() {r := gin.Default()r.MaxMultipartMemory = 8 << 20 // 8 MiBr.Static("/", "./public")r.POST("/upload", func(c *gin.Context) {// 获取用户上传文件file, err := c.FormFile("editormd-image-file")if err != nil {c.JSON(500, gin.H{"message":  "err:"+ err.Error()})return}// file.Filename 就是原始的文件名originalFilename := file.Filename// 做一些处理,例如打印文件名println("Uploaded file: " + originalFilename)src, err2 := file.Open()if err2 != nil {c.JSON(500, gin.H{"message":  "err2:"+ err2.Error()})return}defer src.Close()// 将文件转化为image.Image,添加水印img, _, err3 := image.Decode(src)if err3 != nil {c.JSON(500, gin.H{"message":  "err3:"+ err3.Error()})return}println("%d | %T",img.Bounds().Dx(),img.Bounds().Dx())imgWidth := img.Bounds().Dx()imgHeight := img.Bounds().Dy()dc := gg.NewContext(imgWidth, imgHeight)dc.DrawImage(img, 0, 0)dc.SetRGB(118,104,104)err4 := dc.LoadFontFace("aparaj.ttf", 48)if err4 != nil {c.JSON(500, gin.H{"message": "err4:"+ err4.Error()})return}// 旋转文本 以图像正中为中心旋转dc.RotateAbout(gg.Radians(-40), float64(imgWidth/2), float64(imgHeight/2)) W := int(imgWidth)H := int(imgHeight)text := "https:;//qiucode.cn" //水印文字// 计算水印文本的尺寸textWidth := float64(len(text)) * 20 // 20 是字体大小  可自行修改const S = 60 // 水印文本的大小for y := -H; y < H; y += S {for x := -H; x < W*2; x += int(textWidth) { // 根据文本尺寸调整x轴的绘制间距dc.DrawStringAnchored(text, float64(x), float64(y), 0.5, 0.5)}}//循环添加水印 enddc.SetLineWidth(2)dc.Stroke()// 创建字节缓冲区并将图像编码为PNGbuf := new(bytes.Buffer)err5 :=png.Encode(buf, dc.Image())if err5 != nil {c.JSON(500, gin.H{"message":  "err5:"+ err5.Error()})return}endpoint := "这里是您的 endpoint "  // 这里的是广州区,accessKeyID := "您阿里云的 accessKeyID "   accessKeySecret := "您阿里云 accessKeySecret"   bucketName := "qiucodeimg";//文件存储目录filedir := "golang-test/";// 创建OSS clientclient, err6 := oss.New(endpoint, accessKeyID, accessKeySecret)if err6 != nil {c.JSON(500, gin.H{"message":  "err6:"+ err6.Error()})return}// 获取存储空间bucket, err7 := client.Bucket(bucketName)if err7 != nil {c.JSON(500, gin.H{"message":  "err7:"+ err7.Error()})return}objectKey := filedir + originalFilename// 上传文件流bucket.PutObject(objectKey, bytes.NewReader(buf.Bytes()))// 生成签名的 URLsignedURL, err8 := bucket.SignURL(objectKey, oss.HTTPGet, 600)if err8 != nil {c.JSON(500, gin.H{"message":  "err8 :"+ err8.Error()})return}c.JSON(200, gin.H{"success": 1,"message": "上传成功","url": signedURL, //将成功上传到阿里云OSS的图片 URL 返回给页面})})// 使用  8080 端口进行监听r.Run(":8080")
}

对于以上代码,你看完后,想必有很多话要说吧!没错,可以将图片添加水印的抽取成一个函数,还有的是就是需要判断上传的图片格式,是PNG还是JPEG等格式……

然而,你似乎忘记了,本文已经将原本两个各自独立的,合二为一而且还实现了,至于抽取成函数,上传图片格式判别,就让正在浏览本文的您,或还苦苦找寻怎么使用golang实现对上传图片添加水印并上传到阿里云OSS挠头托腮的!本文的实现将你们的福音,也将是你们的一剂强心针(总算是找到了一篇关于怎么使用golang实现了公司的需求)

差点忘记了,还用静态资源,使用了开源的 editormd。https://github.com/pandao/editor.md 。

在这里插入图片描述

以及 index.html文件内容。

在这里插入图片描述

<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><link href="/static/editormd/css/editormd.css" type="text/css" rel="stylesheet">
</head>
<body><div id="test-editor"><textarea style="display: none;" name="context"></textarea>
</div></body><script src="./static/jquery-3.4.1/jquery-3.4.1.min.js"></script>
<script type="text/javascript" charset="utf-8"  src="./static/editormd/editormd.min.js"></script><script>var editor;$(function () {editor = editormd("test-editor", { //注意1:这里的就是上面的DIV的id属性值width: "90%",height: 750,syncScrolling: "single",path: "/static/editormd/lib/", //注意2:你的路径saveHTMLToTextarea: true ,//注意3:这个配置,方便post提交表单imageUpload : true,imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],imageUploadURL : "/upload",});});
</script></html>

最后,还是贴出效果图。

在这里插入图片描述

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

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

相关文章

当iPhone中的Wi-Fi变灰时别担心,可以尝试这些解决办法

这篇文章解释了当iPhone上的Wi-Fi变灰时,你应该检查并修复的所有事情。 确保飞行模式关闭 这听起来可能很傻,但在执行任何其他操作之前,请确保未打开飞机模式。此功能禁用Wi-Fi(和蜂窝网络),因为它旨在允许你在通常不允许传出无线通信的飞机上使用手机。 查看飞行模式…

Linux 动静态库的制作,使用和加载

Linux 动静态库的制作,使用和加载 一.前置说明1.mylib.h2.mylib.c3.mymath.h mymath.c4.如何制作库 二.动静态库的制作1.静态库的制作1.制作2.使用一下静态库,验证是否成功打包 2.动态库的制作1.编译.c源文件文件生成.o目标文件2.打包生成动态库3.编写makefile文件,自动化制作动…

从0开始打架基于VUE的前端项目

准备与版本 安装nodejs(v20.11.1)安装vue脚手架(@vue/cli 5.0.8) ,参考(https://cli.vuejs.org/zh/)vue版本(2.7.16),vue2的最后一个版本初始化项目 创建一个git项目(可以去gitee/github上创建),注意创建一个空项目创建项目vue create mvp-admin-vue-ui删除自己创建的gi…

OpenHarmony实战:烧录Hi3516DV300开发板

前言 烧录开发板是每个开发者的必修课&#xff0c;每次对系统的修改务必进行烧录测试&#xff0c;确保修改正确和不会引入新问题。 本文基于 Windows10&#xff0c;以 Hi3516DV300 开发板为例&#xff0c;指导如何烧录 OpenHarmony 镜像&#xff0c;镜像也叫固件。 这块开发…

C++ :STL中deque的原理

deque的结构类似于哈希表&#xff0c;使用一个指针数组存储固定大小的数组首地址&#xff0c;当数据分布不均匀时将指针数组内的数据进行偏移&#xff0c;桶不够用的时候会像vector一样扩容然后将之前数组中存储的指针拷贝过来&#xff0c;从原理可以看出deque的性能是非常高的…

ISP-VPN实验

文章目录 ISP-VPN实验一&#xff0c;实验拓扑二、实验要求三、IP规划四、实验配置1、IP配置R1的配置R2的配置R3的配置R4的配置R5的配置 2、配置缺省路由3、认证与被认证配置4、HDLC封装5、构建MGRE和GRE6、整个私有网络基于RIP全网可达7、查看路由配置和PC端配置8、PC端pingR5的…

环境影响与碳排放生命周期评估应用及案例分析

生命周期分析 (Life Cycle Analysis, LCA) 是评价一个产品系统生命周期整个阶段——从原材料的提取和加工&#xff0c;到产品生产、包装、市场营销、使用、再使用和产品维护&#xff0c;直至再循环和最终废物处置——的环境影响的工具。这种方法被认为是一种“从摇篮到坟墓”的…

用JSch实现远程传输文件并打包成jar

本文将简单介绍一下 JSch 这个Java的第三方库的一个简单用法&#xff0c;并以此为实例&#xff0c;讲解 IntelliJ 中打包成 jar 包的2种方式。 实现目标 我们的目标是&#xff0c;做出一个jar包&#xff0c;它能够实现类似于 scp 命令的远程传输文件的功能。用法如下&#xf…

应急响应靶机训练-Linux2题解

前言 接上文&#xff0c;应急响应靶机训练Linux2 靶机地址&#xff1a;应急响应靶机-Linux(2) 题解 登录虚拟机&#xff1a; 修改面板密码 提交攻击者IP 答案&#xff1a;192.168.20.1 查看宝塔日志即可 用的net直接是网关 提交攻击者修改的管理员密码(明文) 答案&…

LeetCode---390周赛

题目列表 3090. 每个字符最多出现两次的最长子字符串 3091. 执行操作使数据元素之和大于等于 K 3092. 最高频率的 ID 3093. 最长公共后缀查询 一、每个字符最多出现两次的最长子字符串 非常经典的滑动窗口问题&#xff0c;即动态维护一段区间&#xff0c;使得这段区间满足…

JUC:park/unpark的用法与原理

park / unpark 用法 // 暂停当前线程 LockSupport.park(); // 恢复某个线程的运行 LockSupport.unpark(暂停线程对象)**先说结论&#xff1a;**无论unpark在park前还是后&#xff0c;都可以解除暂停状态。 先park在unpark可以成功运行&#xff1a; Thread t1 new Thread((…

全局UI方法-弹窗二-列表选择弹窗(ActionSheet)

1、描述 定义列表弹窗 2、接口 ActionSheet.show(value:{ title: string | Resource, message: string | Resource, autoCancel?: boolean, confrim?: {value: string | Resource, action: () > void }, cancel?: () > void, alignment?: DialogAlignment, …

kafka学习笔记02(小滴课堂)

Kafka命令行生产者发送消息和消费者消费消息实战 已存在的kafka不能重复创建。 broker设置的是1&#xff0c;factor大于broker了&#xff0c;所以报错。 生产者发送消息&#xff1a; kafka列表出现了新的kafka。 我们使用这个kafka。 我们启动消费者&#xff1a; 我们现在不从…

【Qt 学习笔记】Day1 | Qt 背景介绍

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Day1 | Qt 背景介绍 文章编号&#xff1a;Qt 学习笔记 / 01 文章目录…

C/C++语言实现简易通讯录 [含文件操作,循环双链表]

文章目录 C/C语言实现简易通讯录概要基本功能运行截图展示主要代码展示 &#x1f396; 博主的CSDN主页&#xff1a;Ryan.Alaskan Malamute &#x1f4dc; 博主的代码仓库主页 [ Gitee ]&#xff1a;ryanala [GitHub]&#xff1a; Ryan-Ala C/C语言实现简易通讯录 ⚠⚠⚠ …

话题通信的python实现

一、发布者Publisher的python实现 step1&#xff1a;在scripts文件夹中创建py节点 step2&#xff1a;第一行是为了指定解释器&#xff0c;Ubuntu20.04是python3&#xff0c;比他低的版本是python。第二行是为了指定编码方式。第五行中&#xff0c;引用index.ros.org中数据类型…

RVM安装ruby笔记

环境 硬件&#xff1a;Macbook Pro 系统&#xff1a;macOS 14.1 安装公钥 通过gpg安装公钥失败&#xff0c;报错如下&#xff1a; 换了几个公钥地址&#xff08;hkp://subkeys.pgp.net&#xff0c;hkp://keys.gnupg.net&#xff0c;hkp://pgp.mit.edu&#xff09;&#xff0c;…

论文笔记:Retrieval-Augmented Generation forAI-Generated Content: A Survey

北大202402的RAG综述 1 intro 1.1 AICG 近年来&#xff0c;人们对人工智能生成内容&#xff08;AIGC&#xff09;的兴趣激增。各种内容生成工具已经精心设计&#xff0c;用于生产各种模态下的多样化对象 文本&代码&#xff1a;大型语言模型&#xff08;LLM&#xff09;…

Java:链表

一、链表简介 1、链表与顺序表的区别 上一篇博客我介绍了顺序表&#xff0c;这次我们来认识认识链表&#xff01;先来看看二者的区别&#xff1a; 顺序表&#xff1a;由于顺序表实际上是一个数组&#xff0c;因此它在物理上是连续的&#xff0c;逻辑上也是连续的&#xff01; …

【IDEA】使用debug方式去运行java程序

什么是debug工具&#xff1f; 调试工具&#xff08;debug工具&#xff09;是一种用于帮助程序员识别和修复程序中的错误的工具。它们提供了一系列的功能&#xff0c;帮助程序员在代码执行的过程中跟踪和检测问题&#xff0c;例如查看变量的值、检查函数的调用栈、设置断点来停…