使用 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 main

import (
    "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 MiB
	r.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)
            }
        }
      //循环添加水印 end

        dc.SetLineWidth(2)
        dc.Stroke()
     
        // 创建字节缓冲区并将图像编码为PNG
        buf := 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 client
        client, 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()))

         // 生成签名的 URL
         signedURL, 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>

最后,还是贴出效果图。

在这里插入图片描述

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

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

相关文章

【个人笔记】python界面美化

目录 标题栏美化 样例展示 代码 配套鼠标移动 完整展示 标题栏美化 样例展示 代码 import tkinter as tk from tkinter import ttk from PIL import Image, ImageTk import subprocess import sysdef open_buy_quantity():window.destroy()subprocess.run(["p…

网际协议 - IP

文章目录 目录 文章目录 前言 1 . 网际协议IP 1.1 网络层和数据链路层的关系 2. IP基础知识 2.1 什么是IP地址? 2.2 路由控制 3. IP地址基础知识 3.1 IP地址定义 3.2 IP地址组成 3.3 IP地址分类 3.4 子网掩码 IP地址分类导致浪费? 子网与子网掩码 3.5 CIDR与…

记录个人学习golang路线(如何学习golang,如何转golang)

最近好久没更&#xff0c;在看兔兔的博客&#xff0c;学习golang&#xff0c;兔兔的文章&#xff0c;有一定的编程经验 && 初学golang者&#xff0c;一定要看&#xff0c;如果是其他语言转golang&#xff0c;那就必须要看了&#xff0c;可以帮助你了解golang的语法&…

BC40056 Imports“SolidWorks.Interop.swconst”中指定的命名空间或类型不包含任何公共成员

BC40056 Imports“SolidWorks.Interop.swconst”中指定的命名空间或类型不包含任何公共成员&#xff0c;或者找不到该命名空间或类型。 问题描述原因分析 解决办法 ) 问题描述 严重性 代码 说明 项目 文件 行 警告 BC40056 Imports“SolidWorks.Interop.swconst”中指定的命名…

单链表就地逆置

算法思想&#xff1a;构建一个带头结点的单链表L&#xff0c;然后访问链表中的每一个数据结点&#xff0c;将访问到的数据结点依此插入到L的头节点之后。 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> typedef int ElemType; typedef s…

ElasticSearch开发指北和场景题分析

前言 本篇是ES系列的第二篇&#xff0c;继上次的理论篇ElasticSearch理论体系构建后&#xff0c;带来了实战篇。实战篇来自于我对常见操作以及场景的分析总结&#xff0c;详细到每个步骤和理由&#xff0c;下一篇将是性能优化篇。 常用操作 以下操作均使用ES的API进行展示&a…

金融投贷通--功能测试分析与设计

金融投贷通功能测试分析与设计 测试点分析借款业务测试点投资业务测试点 测试用例借款业务测试用例投资业务测试用例 缺陷面试题 测试报告 测试点分析 借款业务测试点 投资业务测试点 测试用例 借款业务测试用例 借款成功&#xff08;主业务&#xff09;、借款成功&#xff…

Figma:如何在数据库规模四年增长近100倍的挑战中“活”下来?

在当今数字化飞速发展的时代&#xff0c;大数据的崛起已成为各行业不可或缺的重要驱动力。然而&#xff0c;随着数据量的激增&#xff0c;许多企业面临着巨大的挑战&#xff0c;尤其是在数据库管理和维护方面。Figma作为一家专注于设计协作领域的领先企业&#xff0c;在过去的四…

Tron波场区块链 | 使用Java将Tron钱包助记词转私钥 全网独门一份

如何使用Java将Tron钱包助记词转换为私钥? 本来想着这个问题挺简单&#xff0c;可是查了半天&#xff0c;不是&#xff0c;不止半天查了好长时间&#xff0c;看了半天官网文档&#xff0c;全网Java就没有实现的。 咋办。。。咋办呢&#xff1f; 好巧&#xff0c;官网我看到…

瑞吉外卖实战学习--5、新增员工功能

新增员工功能 效果图1、开发流程2、页面发送ajax请求,将新增员工的信息以json的形式提交给服务器2.1、在填写信息的时候会发现身份校验比较麻烦,可以在validate中将全局的校验方式去掉,方便填写2.3、看到接口未employee2.4、前端代码分析3、服务器接收到提交的数据并调用ser…

Kotlin 中的类和构造方法

Kotlin 中的类与接口和 Java 中的类与接口还是有区别的。例如&#xff0c;Koltin 中的接口可以包含属性声明&#xff0c;与 Java 不同的是。Kotlin 的声明默认是 final 和 public 的。此外&#xff0c;嵌套的类默认并不是内部类&#xff1a;它们并没有包含对其它外部类的隐式引…

【系统架构师】-第18章-安全架构设计

(1)信息泄露&#xff1a;信息被泄露或透露给某个非授权的实体。 (2)破坏信息的完整性&#xff1a;数据被非授权地进行增删、修改或破坏而受到损失。 (3)拒绝服务&#xff1a;对信息或其他资源的合法访问被无条件地阻止。 (4)非法使用(非授权访问):某一资源被某个非授权的人或…

第十五届蓝桥杯模拟考试II_物联网设计

反思&#xff1a; 本次模拟让我惊醒&#xff0c;写这个作品如同搭积木&#xff0c;在拼接的时候都要仔细检查这个积木是否出bug,确保没有问题再将其拼接到之前搭好的大模块之中&#xff0c;因为就是这样的题目我在处理过程中就遇到了BUG&#xff0c;原因竟出在输入模式要上拉&…

二十四种设计模式与六大设计原则(二):【门面模式、适配器模式、模板方法模式、建造者模式、桥梁模式、命令模式】的定义、举例说明、核心思想、适用场景和优缺点

接上次博客&#xff1a;二十四种设计模式与六大设计原则&#xff08;一&#xff09;&#xff1a;【策略模式、代理模式、单例模式、多例模式、工厂方法模式、抽象工厂模式】的定义、举例说明、核心思想、适用场景和优缺点-CSDN博客 目录 门面模式【Facade Pattern】 定义 举…

Arthas线上排查问题流程

入门文档&#xff1a;trace | arthas 1、jar下载和启动 连接curl -O https://arthas.aliyun.com/arthas-boot.jar【wget https://arthas.aliyun.com/arthas-boot.jar】 。.../jdk/bin/java -jar arthas-boot.jar 22336【最好在这个目录启动,port可选】 选择进程序号 enter回车…

二分(二段性)

本文用于记录个人算法竞赛学习&#xff0c;仅供参考 一.二分算法 二分算法一般用于具有二段性的问题&#xff0c;数据不一定具有单调性&#xff0c;所以单调可二分&#xff0c;可二分不一定就要单调。 二.整数二分 1. 模板一&#xff1a;将区间[l, r]划分为[l, mid] 和 [mid…

学生价,leetcode会员购买分析

最近想要购买leetcode会员&#xff0c;但不知道买啥好&#xff0c;打算用python可视化数据进行一个简单的分析 具体数据如下 curve 1: 首两月79元每月&#xff0c;后续连续包月59curve 2: 90天199curve 3: 365天365&#xff08;学生认证&#xff09; 这么看&#xff0c;数据…

FA模型切换Stage模型组件切换之ServiceAbility切换DataAbility切换

ServiceAbility切换 FA模型中的ServiceAbility对应Stage模型中的ServiceExtensionAbility。Stage模型下的ServiceExtensionAbility为系统API&#xff0c;只有系统应用才可以创建。因此&#xff0c;FA模型的ServiceAbility的切换&#xff0c;对于系统应用和三方应用策略有所不同…

AI制作一键生成模特换装照,电商蓝海副业供不应求

在电子商务领域&#xff0c;产品展示和模特试穿照片是至关重要的。在传统流程中&#xff0c;创建一张精美的商品主图通常需要摄影师在摄影棚里进行白底拍摄&#xff0c;接着设计师会进行图像设计处理。 公 重 号&#xff1a;老A程序站 对于更复杂且高端的商品展示&#xff0c…

测试小萌新都看得懂的使用JMeter进行压测

前言 需要先搭配好JMeter的环境并运行 准备一个被测试接口 对其进行压力测试 搭配JMeter的运行环境 1.安装jdk jdk安装过程会提供两次安装&#xff0c;第一次是安装jre&#xff0c;第二次是安装java。 我在D盘提前新建了2个文件夹&#xff0c;jre文件夹用于jre安装&#xff…