Go发布自定义包

农妇山泉一亩田  金牌会员 | 2024-8-30 23:52:41 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 857|帖子 857|积分 2571

1、初始化go.mod

  1. go mod init github.com/xumeng03/images
复制代码
2、编写包内容

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

  1. package images
  2. import (
  3.         "image"
  4.         "io"
  5.         "os"
  6.         "path"
  7.         "strings"
  8. )
  9. type FileSystem interface {
  10.         Create(string) (io.WriteCloser, error)
  11.         Open(string) (io.ReadCloser, error)
  12. }
  13. type LocalFileSystem struct{}
  14. func (fs LocalFileSystem) Create(name string) (io.WriteCloser, error) {
  15.         return os.Create(name)
  16. }
  17. func (fs LocalFileSystem) Open(name string) (io.ReadCloser, error) {
  18.         return os.Open(name)
  19. }
  20. var fs FileSystem = LocalFileSystem{}
  21. func Open(filename string) (image.Image, error) {
  22.         file, err := fs.Open(filename)
  23.         if err != nil {
  24.                 return nil, err
  25.         }
  26.         defer file.Close()
  27.         return Decode(file)
  28. }
  29. func Close(img image.Image, filename string, quality int) error {
  30.         file, err := fs.Create(filename)
  31.         if err != nil {
  32.                 return err
  33.         }
  34.         ext := path.Ext(filename)
  35.         err = Encode(file, img, strings.ReplaceAll(ext, ".", ""), quality)
  36.         if err != nil {
  37.                 return err
  38.         }
  39.         err = file.Close()
  40.         return err
  41. }
复制代码
2.2、image.go

  1. package images
  2. import (
  3.     "fmt"
  4.     "image"
  5.     "image/jpeg"
  6.     _ "image/jpeg"
  7.     _ "image/png"
  8.     "io"
  9. )
  10. func Decode(reader io.Reader) (image.Image, error) {
  11.     // 数据写入 PipeWriter 对象后,可以通过相应的 PipeReader 对象进行读取;
  12.     pr, pw := io.Pipe()
  13.     // 创建了一个新的 io.Reader 对象,这个对象能够将从其读取的数据同时写入到另一个 io.Writer 中(如同包装类)
  14.     reader = io.TeeReader(reader, pw)
  15.     done := make(chan struct{})
  16.     var orient orientation
  17.     go func() {
  18.        defer close(done)
  19.        orient = readOrientation(pr)
  20.        io.Copy(io.Discard, pr)
  21.     }()
  22.     img, _, err := image.Decode(reader)
  23.     pw.Close()
  24.     <-done
  25.     fmt.Println(orient)
  26.     if err != nil {
  27.        return nil, err
  28.     }
  29.     return img, nil
  30. }
  31. func Encode(w io.Writer, img image.Image, t string, quality int) error {
  32.     switch t {
  33.     case "jpg":
  34.        fallthrough
  35.     case "jpeg":
  36.        if nrgba, ok := img.(*image.NRGBA); ok && nrgba.Opaque() {
  37.           rgba := &image.RGBA{
  38.              Pix:    nrgba.Pix,
  39.              Stride: nrgba.Stride,
  40.              Rect:   nrgba.Rect,
  41.           }
  42.           return jpeg.Encode(w, rgba, &jpeg.Options{Quality: quality})
  43.        }
  44.        return jpeg.Encode(w, img, &jpeg.Options{Quality: quality})
  45.     default:
  46.        println("type error!")
  47.        return nil
  48.     }
  49. }
复制代码
2.3、exif.go

  1. package images
  2. import (
  3.         "encoding/binary"
  4.         "io"
  5. )
  6. type orientation int
  7. const (
  8.         // 方向未指定
  9.         orientationUnspecified = 0
  10.         // 正常方向
  11.         orientationNormal = 1
  12.         // 需水平翻转
  13.         orientationFlipH = 2
  14.         // 需旋转180度
  15.         orientationRotate180 = 3
  16.         // 需垂直翻转
  17.         orientationFlipV = 4
  18.         // 需对角线翻转(左上到右下)
  19.         orientationTranspose = 5
  20.         // 需逆时针旋转270度
  21.         orientationRotate270 = 6
  22.         // 需对角线翻转(右上到左下)
  23.         orientationTransverse = 7
  24.         // 需顺时针旋转90度
  25.         orientationRotate90 = 8
  26. )
  27. const (
  28.         // Start Of Image:表示 JPEG 图片流的起始
  29.         markerSOI = 0xffd8
  30.         // Application Segment 1:表示 APP1 区块,EXIF 信息通常存储在 APP1 区块内
  31.         markerAPP1 = 0xffe1
  32.         // Exif Header:表示 APP1 区块确实包含了 EXIF 信息(紧跟在 APP1 区块标识后),且后面通常跟着两个填充字节
  33.         exifHeader = 0x45786966
  34.         // Big Endian byte order mark:如果 EXIF 段使用大端字节序,那么其字节序标记为 'MM' (0x4D4D),即(高位字节排在前)
  35.         byteOrderBE = 0x4d4d
  36.         // Little Endian byte order mark:如果 EXIF 段使用小端字节序,那么其字节序标记为 'II' (0x4949),即(低位字节排在前)
  37.         byteOrderLE = 0x4949
  38.         // Orientation Tag:表示图像的方向
  39.         orientationTag = 0x0112
  40. )
  41. func readOrientation(reader io.Reader) orientation {
  42.         // 检查 JPEG 开始标记(PNG 和 GIF 等格式不是传统意义上的摄影,图像元数据一般不包括拍摄方向信息。处理这些图像文件时,通常没有必要读取或调整图像方向)
  43.         var soi uint16
  44.         if binary.Read(reader, binary.BigEndian, &soi) != nil {
  45.                 return orientationUnspecified
  46.         }
  47.         if soi != markerSOI {
  48.                 return orientationUnspecified
  49.         }
  50.         for {
  51.                 var marker, size uint16
  52.                 if err := binary.Read(reader, binary.BigEndian, &marker); err != nil {
  53.                         return orientationUnspecified
  54.                 }
  55.                 if err := binary.Read(reader, binary.BigEndian, &size); err != nil {
  56.                         return orientationUnspecified
  57.                 }
  58.                 // 检查是否是有效的 JPEG 标记
  59.                 if marker>>8 != 0xff {
  60.                         return orientationUnspecified
  61.                 }
  62.                 // 检查是否为 APP1 标记
  63.                 if marker == markerAPP1 {
  64.                         break
  65.                 }
  66.                 // 对于任何 JPEG 数据块,其报告的大小应至少为2字节
  67.                 if size < 2 {
  68.                         return orientationUnspecified
  69.                 }
  70.                 // 这里的减2表示减去size本身占用的2字节(size表示的是从size开始这个段还有几个字节)
  71.                 if _, err := io.CopyN(io.Discard, reader, int64(size-2)); err != nil {
  72.                         return orientationUnspecified
  73.                 }
  74.         }
  75.         // 检查 exifHeader 标记
  76.         var header uint32
  77.         if err := binary.Read(reader, binary.BigEndian, &header); err != nil {
  78.                 return orientationUnspecified
  79.         }
  80.         if header != exifHeader {
  81.                 return orientationUnspecified
  82.         }
  83.         if _, err := io.CopyN(io.Discard, reader, 2); err != nil {
  84.                 return orientationUnspecified
  85.         }
  86.         // 从文件中读取的字节序标识
  87.         var byteOrderTag uint16
  88.         var byteOrder binary.ByteOrder
  89.         if err := binary.Read(reader, binary.BigEndian, &byteOrderTag); err != nil {
  90.                 return orientationUnspecified
  91.         }
  92.         switch byteOrderTag {
  93.         case byteOrderBE:
  94.                 byteOrder = binary.BigEndian
  95.         case byteOrderLE:
  96.                 byteOrder = binary.LittleEndian
  97.         default:
  98.                 return orientationUnspecified
  99.         }
  100.         if _, err := io.CopyN(io.Discard, reader, 2); err != nil {
  101.                 return orientationUnspecified
  102.         }
  103.         // 跳过 exif 段
  104.         var offset uint32
  105.         if err := binary.Read(reader, binary.BigEndian, &offset); err != nil {
  106.                 return orientationUnspecified
  107.         }
  108.         if offset < 8 {
  109.                 // 在 TIFF 格式中,如果 offset 小于 8(byteOrderTag、填充字节、offset字节),那么它指向的位置是不合逻辑的,表明可能是一个损坏或非法格式的文件。
  110.                 return orientationUnspecified
  111.         }
  112.         if _, err := io.CopyN(io.Discard, reader, int64(offset-8)); err != nil {
  113.                 return orientationUnspecified
  114.         }
  115.         // 获取标签数
  116.         var numTags uint16
  117.         if err := binary.Read(reader, byteOrder, &numTags); err != nil {
  118.                 return orientationUnspecified
  119.         }
  120.         for i := 0; i < int(numTags); i++ {
  121.                 var tag uint16
  122.                 if err := binary.Read(reader, binary.BigEndian, &tag); err != nil {
  123.                         return orientationUnspecified
  124.                 }
  125.                 if tag != orientationTag {
  126.                         // 10 = 2(数据类型)+ 4(计数)+ 4(值或值偏移量)
  127.                         if _, err := io.CopyN(io.Discard, reader, 10); err != nil {
  128.                                 return orientationUnspecified
  129.                         }
  130.                         continue
  131.                 }
  132.                 // 跳过2字节(数据类型)+ 4字节(计数)
  133.                 if _, err := io.CopyN(io.Discard, reader, 6); err != nil {
  134.                         return orientationUnspecified
  135.                 }
  136.                 // 读取方向值(在 TIFF 中,实际的方向值可以直接存放在“值或值偏移量”的位置,并且仅占用前两字节,剩余的两字节则不会包含任何重要信息)
  137.                 var direction uint16
  138.                 if err := binary.Read(reader, binary.BigEndian, &direction); err != nil {
  139.                         return orientationUnspecified
  140.                 }
  141.                 if direction < 1 || direction > 8 {
  142.                         // EXIF 规范定义的图像方向值应该在 1 到 8 之间
  143.                         return orientationUnspecified
  144.                 }
  145.                 return orientation(direction)
  146.         }
  147.         return orientationUnspecified
  148. }
复制代码
3、测试

3.1、fs_test.go

  1. package images
  2. import (
  3.         "fmt"
  4.         "testing"
  5. )
  6. func TestOpen(t *testing.T) {
  7.         fileName := "test.jpeg"
  8.         _, err := Open(fileName)
  9.         if err != nil {
  10.                 fmt.Println(err)
  11.                 return
  12.         }
  13.         fmt.Println(fileName, "读取成功")
  14. }
  15. func TestClose(t *testing.T) {
  16.         fileName := "test.jpeg"
  17.         quality := 50
  18.         img, err := Open(fileName)
  19.         if err != nil {
  20.                 fmt.Println(err)
  21.                 return
  22.         }
  23.         err = Close(img, "compress_"+fileName, quality)
  24.         if err != nil {
  25.                 fmt.Println(err)
  26.                 return
  27.         }
  28.         fmt.Println(fileName, "保存成功")
  29. }
复制代码
4、发布

创建一个新的tag:v0.0.1


5、使用

  1. go get -u github.com/xumeng03/images
复制代码
  1. package main
  2. import (
  3.         "fmt"
  4.         "github.com/xumeng03/images"
  5. )
  6. func main() {
  7.         fileName := "test.jpeg"
  8.         quality := 50
  9.         img, err := images.Open(fileName)
  10.         if err != nil {
  11.                 fmt.Println(err)
  12.                 return
  13.         }
  14.         err = images.Close(img, "compress_"+fileName, quality)
  15.         if err != nil {
  16.                 fmt.Println(err)
  17.                 return
  18.         }
  19.         fmt.Println(fileName, "压缩成功")
  20. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

农妇山泉一亩田

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表