Go发布自定义包

1、初始化go.mod

go mod init github.com/xumeng03/images

2、编写包内容

这里只是一个简单的压缩jpg/jpeg图片例子,代码参考 https://github.com/disintegration/imaging

2.1、fs.go

package imagesimport ("image""io""os""path""strings"
)type FileSystem interface {Create(string) (io.WriteCloser, error)Open(string) (io.ReadCloser, error)
}type LocalFileSystem struct{}func (fs LocalFileSystem) Create(name string) (io.WriteCloser, error) {return os.Create(name)
}func (fs LocalFileSystem) Open(name string) (io.ReadCloser, error) {return os.Open(name)
}var fs FileSystem = LocalFileSystem{}func Open(filename string) (image.Image, error) {file, err := fs.Open(filename)if err != nil {return nil, err}defer file.Close()return Decode(file)
}func Close(img image.Image, filename string, quality int) error {file, err := fs.Create(filename)if err != nil {return err}ext := path.Ext(filename)err = Encode(file, img, strings.ReplaceAll(ext, ".", ""), quality)if err != nil {return err}err = file.Close()return err
}

2.2、image.go

package imagesimport ("fmt""image""image/jpeg"_ "image/jpeg"_ "image/png""io"
)func Decode(reader io.Reader) (image.Image, error) {// 数据写入 PipeWriter 对象后,可以通过相应的 PipeReader 对象进行读取;pr, pw := io.Pipe()// 创建了一个新的 io.Reader 对象,这个对象能够将从其读取的数据同时写入到另一个 io.Writer 中(如同包装类)reader = io.TeeReader(reader, pw)done := make(chan struct{})var orient orientationgo func() {defer close(done)orient = readOrientation(pr)io.Copy(io.Discard, pr)}()img, _, err := image.Decode(reader)pw.Close()<-donefmt.Println(orient)if err != nil {return nil, err}return img, nil
}func Encode(w io.Writer, img image.Image, t string, quality int) error {switch t {case "jpg":fallthroughcase "jpeg":if nrgba, ok := img.(*image.NRGBA); ok && nrgba.Opaque() {rgba := &image.RGBA{Pix:    nrgba.Pix,Stride: nrgba.Stride,Rect:   nrgba.Rect,}return jpeg.Encode(w, rgba, &jpeg.Options{Quality: quality})}return jpeg.Encode(w, img, &jpeg.Options{Quality: quality})default:println("type error!")return nil}
}

2.3、exif.go

package imagesimport ("encoding/binary""io"
)type orientation intconst (// 方向未指定orientationUnspecified = 0// 正常方向orientationNormal = 1// 需水平翻转orientationFlipH = 2// 需旋转180度orientationRotate180 = 3// 需垂直翻转orientationFlipV = 4// 需对角线翻转(左上到右下)orientationTranspose = 5// 需逆时针旋转270度orientationRotate270 = 6// 需对角线翻转(右上到左下)orientationTransverse = 7// 需顺时针旋转90度orientationRotate90 = 8
)const (// Start Of Image:表示 JPEG 图片流的起始markerSOI = 0xffd8// Application Segment 1:表示 APP1 区块,EXIF 信息通常存储在 APP1 区块内markerAPP1 = 0xffe1// Exif Header:表示 APP1 区块确实包含了 EXIF 信息(紧跟在 APP1 区块标识后),且后面通常跟着两个填充字节exifHeader = 0x45786966// Big Endian byte order mark:如果 EXIF 段使用大端字节序,那么其字节序标记为 'MM' (0x4D4D),即(高位字节排在前)byteOrderBE = 0x4d4d// Little Endian byte order mark:如果 EXIF 段使用小端字节序,那么其字节序标记为 'II' (0x4949),即(低位字节排在前)byteOrderLE = 0x4949// Orientation Tag:表示图像的方向orientationTag = 0x0112
)func readOrientation(reader io.Reader) orientation {// 检查 JPEG 开始标记(PNG 和 GIF 等格式不是传统意义上的摄影,图像元数据一般不包括拍摄方向信息。处理这些图像文件时,通常没有必要读取或调整图像方向)var soi uint16if binary.Read(reader, binary.BigEndian, &soi) != nil {return orientationUnspecified}if soi != markerSOI {return orientationUnspecified}for {var marker, size uint16if err := binary.Read(reader, binary.BigEndian, &marker); err != nil {return orientationUnspecified}if err := binary.Read(reader, binary.BigEndian, &size); err != nil {return orientationUnspecified}// 检查是否是有效的 JPEG 标记if marker>>8 != 0xff {return orientationUnspecified}// 检查是否为 APP1 标记if marker == markerAPP1 {break}// 对于任何 JPEG 数据块,其报告的大小应至少为2字节if size < 2 {return orientationUnspecified}// 这里的减2表示减去size本身占用的2字节(size表示的是从size开始这个段还有几个字节)if _, err := io.CopyN(io.Discard, reader, int64(size-2)); err != nil {return orientationUnspecified}}// 检查 exifHeader 标记var header uint32if err := binary.Read(reader, binary.BigEndian, &header); err != nil {return orientationUnspecified}if header != exifHeader {return orientationUnspecified}if _, err := io.CopyN(io.Discard, reader, 2); err != nil {return orientationUnspecified}// 从文件中读取的字节序标识var byteOrderTag uint16var byteOrder binary.ByteOrderif err := binary.Read(reader, binary.BigEndian, &byteOrderTag); err != nil {return orientationUnspecified}switch byteOrderTag {case byteOrderBE:byteOrder = binary.BigEndiancase byteOrderLE:byteOrder = binary.LittleEndiandefault:return orientationUnspecified}if _, err := io.CopyN(io.Discard, reader, 2); err != nil {return orientationUnspecified}// 跳过 exif 段var offset uint32if err := binary.Read(reader, binary.BigEndian, &offset); err != nil {return orientationUnspecified}if offset < 8 {// 在 TIFF 格式中,如果 offset 小于 8(byteOrderTag、填充字节、offset字节),那么它指向的位置是不合逻辑的,表明可能是一个损坏或非法格式的文件。return orientationUnspecified}if _, err := io.CopyN(io.Discard, reader, int64(offset-8)); err != nil {return orientationUnspecified}// 获取标签数var numTags uint16if err := binary.Read(reader, byteOrder, &numTags); err != nil {return orientationUnspecified}for i := 0; i < int(numTags); i++ {var tag uint16if err := binary.Read(reader, binary.BigEndian, &tag); err != nil {return orientationUnspecified}if tag != orientationTag {// 10 = 2(数据类型)+ 4(计数)+ 4(值或值偏移量)if _, err := io.CopyN(io.Discard, reader, 10); err != nil {return orientationUnspecified}continue}// 跳过2字节(数据类型)+ 4字节(计数)if _, err := io.CopyN(io.Discard, reader, 6); err != nil {return orientationUnspecified}// 读取方向值(在 TIFF 中,实际的方向值可以直接存放在“值或值偏移量”的位置,并且仅占用前两字节,剩余的两字节则不会包含任何重要信息)var direction uint16if err := binary.Read(reader, binary.BigEndian, &direction); err != nil {return orientationUnspecified}if direction < 1 || direction > 8 {// EXIF 规范定义的图像方向值应该在 1 到 8 之间return orientationUnspecified}return orientation(direction)}return orientationUnspecified
}

3、测试

3.1、fs_test.go

package imagesimport ("fmt""testing"
)func TestOpen(t *testing.T) {fileName := "test.jpeg"_, err := Open(fileName)if err != nil {fmt.Println(err)return}fmt.Println(fileName, "读取成功")
}func TestClose(t *testing.T) {fileName := "test.jpeg"quality := 50img, err := Open(fileName)if err != nil {fmt.Println(err)return}err = Close(img, "compress_"+fileName, quality)if err != nil {fmt.Println(err)return}fmt.Println(fileName, "保存成功")
}

4、发布

创建一个新的tag:v0.0.1

在这里插入图片描述
在这里插入图片描述

5、使用

go get -u github.com/xumeng03/images
package mainimport ("fmt""github.com/xumeng03/images"
)func main() {fileName := "test.jpeg"quality := 50img, err := images.Open(fileName)if err != nil {fmt.Println(err)return}err = images.Close(img, "compress_"+fileName, quality)if err != nil {fmt.Println(err)return}fmt.Println(fileName, "压缩成功")
}

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

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

相关文章

Vue3中的defineExpose的认识

文章目录 defineExpose子组件父组件&#xff1a;总结&#xff1a; defineExpose 使用 <script setup> 的组件是默认关闭的——即通过模板引用或者 $parent 链获取到的组件的公开实例&#xff0c;** 不会 **暴露任何在 <script setup> 中声明的绑定。 可以通过 def…

OpenCV几何图像变换(10)透视变换函数warpPerspective()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 warpPerspective 函数使用指定的矩阵对源图像进行透视变换&#xff1a; dst ( x , y ) src ( M 11 x M 12 y M 13 M 31 x M 32 y M 33 , M…

必备工具:2024四大视频压缩神器推荐!

随着视频质量的不断提高&#xff0c;文件大小也变得越来越大&#xff0c;这给存储和分享带来了不小的挑战。今天&#xff0c;我们就来一起探索几款优秀的视频压缩工具&#xff01; 福昕视频压缩大师 直达链接&#xff1a;www.foxitsoftware.cn/shipinyasuo/ 福昕视频压缩大师…

两个月冲刺软考——SQL基础:排序、分组和聚合函数的实用指南

1.涉及到的部分基本语法 1.1 ORDER BY 与 GROUP BY ORDER BY用于对查询结果进行排序&#xff1b;默认是升序&#xff08;ASC&#xff09;&#xff0c;可以指定降序&#xff08;DESC&#xff09;。 GROUP BY用于将数据按照一个或多个列进行分组&#xff1b;通常与聚合函数&am…

长视频平台:谁都想成为「爆款制造机」

【潮汐商业评论/ 原创】 “今晚下班回家&#xff0c;我要先追优酷更新的《边水往事》&#xff0c;再补爱奇艺的《九部检察官》&#xff0c;还有腾讯视频新上线的脱口秀……”对于Chloe来说&#xff0c;没有什么比下班回家看剧更放松的。 “为了追这些剧&#xff0c;我最近把优…

苹果秋季发布会前瞻:iPhone 16领衔新品盛宴

苹果定档9月9日&#xff0c;揭开新品神秘面纱 苹果公司近日正式宣布&#xff0c;将于9月9日在加州库比蒂诺的Apple Park&#xff0c;史蒂夫乔布斯剧院举办年度秋季新品发布会&#xff0c;主题为“It’s Glowtime”&#xff0c;预示着Siri界面将迎来一场华丽变身。此次发布会较原…

米壳AI:发现一个可以保存国内外视频的宝藏网站!

&#x1f388;嘿&#xff0c;朋友们&#xff01;今天我要给大家分享一个超级棒的发现 —— 一个可以下载国内外视频的网站&#xff01;&#x1f389; 在这个信息爆炸的时代&#xff0c;我们总是渴望能够随时随地欣赏到精彩的视频内容。 无论是国内的热门影视剧、搞笑短视频&am…

Docmatix - 超大文档视觉问答数据集

本文&#xff0c;我们将发布Docmatix - 一个超大的文档视觉问答 (DocVQA) 数据集&#xff0c;比之前的数据集大 100 倍。当使用 Docmatix 微调 Florence-2 时&#xff0c;消融实验显示 DocVQA 任务的性能提高了 20%。 Docmatix - 一个超大的文档视觉问答 (DocVQA) 数据集https:…

虚幻5|按键触发学习

一&#xff0c;如图参考 1.下移 驱动阈值 越大按时间长才会触发&#xff0c;越小很快就可以触发 2.按下 当按下超出驱动阈值大小就会触发一次&#xff0c;这里的驱动阈值只能设置再0.1~1的大小 3.已松开 当按下的时候&#xff0c;先触发单次的started&#xff0c;如果按压…

#QT 笔记一

重点&#xff1a;面试考试大概率涉及&#xff0c;需要不借助任何资料掌握。掌握&#xff1a;面试考试可能涉及&#xff0c;需要不借助任何资料掌握。熟悉&#xff1a;面试考试可能涉及&#xff0c;可以稍微参考资料掌握。了解&#xff1a;面试考试小概率涉及&#xff0c;面试拔…

智能优化特征选择|基于鹦鹉优化(2024年新出优化算法)的特征选择(分类器选用的是KNN)研究Matlab程序 【优化算法可以替换成其他优化方法】

智能优化特征选择|基于鹦鹉优化&#xff08;2024年新出优化算法&#xff09;的特征选择&#xff08;分类器选用的是KNN&#xff09;研究Matlab程序 【优化算法可以替换成其他优化方法】 文章目录 一、PO基本原理PO基本原理基本流程示例应用 二、实验结果三、核心代码四、代码获…

某张卡NR only下可以驻网 AUTO模式下在2G/3G工作

结论:需要终端设置为data centric&#xff0c;或ps only。 从日志看&#xff0c;5G下的注册收到REGISTRATION ACCEPT。但是没有携带ims support。 正常网络&#xff1a; ims_Vops_3GPP 1 (0x1) nwk_feature_supp_inc 1 (0x1)nwk_feature_supportedlength 2 (0x2)mpsi 0 (0…

【HarmonyOS NEXT开发】鸿蒙开发环境准备,ArkTS基础语法入门

文章目录 鸿蒙开发环境准备&#xff0c;ArkTS基础语法入门大纲简介DevEco Studio简介运行环境要求 安装与配置开发工具下载Harmony OS 和 OpenHarmony 的区别Previewer汉化插件的配置 ArkTS基础快速入门1. 解释说明2. 变量与常量3. 变量命名规则4. 数组5. 函数定义函数调用函数…

Win10 安装 Rabbitmq

参考文档&#xff1a;https://www.rabbitmq.com/docs/install-windows 一、安装 Erlang 语言 安装 RabbitMQ 需要该语言的支持才能安装 下载地址&#xff1a;https://erlang.org/download/otp_versions_tree.html 点击这里下载最新版本&#xff1a;27.0.1 直接默认 next 更…

2d像素游戏基本架构

目录 2D像素游戏的基本架构通常包括以下几个关键部分 Unity和虚幻引擎在2D游戏开发中的性能比较 Unity的2D工具设计复杂的地图和场景 创建和管理地图资源&#xff1a; 使用TileMap工具&#xff1a; 构建复杂场景&#xff1a; 添加碰撞体和物理效果&#xff1a; 优化和…

Android settings命令讲解和实战

1&#xff0c;简介 在Android系统中&#xff0c;settings命令用于管理设备设置。这些命令可以与Settings提供者&#xff08;Settings provider&#xff09;交互&#xff0c;后者是一个用于存储和检索系统设置的系统服务。Settings provider在Android系统中可以被看作是一个特殊…

携程:从MySQL迁移OceanBase的数据库发布系统实践

作者简介&#xff1a;杨晓军 现就职于携程的数据库团队&#xff0c;主要负责携程数据库的研发与管理&#xff0c;专注于提升数据库的稳定性。 自分布式关系型数据库OceanBase开源以来&#xff0c;携程已经在线上环境中进行了广泛的应用&#xff0c;取代了原先以MySQL为主力的业…

组件通信 Vue3

1.props 1.child <template><div class"child"><h3>子组件</h3><h4>玩具&#xff1a;{{ toy }}</h4><h4>父给的车&#xff1a;{{ car }}</h4><button click"sendToy(toy)">把玩具给父亲</butt…

通过visual studio进行dump文件调试和分析

0、前言 很多时候程序crash之后需要分析原因。对于C/C程序&#xff0c;一般最常见的场景和方法就是根据dump文件进行分析。 1、分析的前提条件 进行dump文件分析&#xff0c;需要以下文件&#xff1a; 进程crash时产生的dump文件程序源码进程对应的程序exe文件编译exe文件时产…

【赵渝强老师】MongoDB的存储引擎

存储引擎&#xff08;Storage Engine&#xff09;是MongoDB的核心组件&#xff0c;它负责管理数据如何存储在硬盘&#xff08;Disk&#xff09;和内存&#xff08;Memory&#xff09;上。从MongoDB 3.2 版本开始&#xff0c;MongoDB支持多种类型的数据存储引擎。 视频讲解如下&…