我是靠谱客的博主 名字长了才好记,这篇文章主要介绍golang实现一个图片上传上传裁剪并加水印功能,现在分享给大家,希望可以做个参考。

下面是一个完整的 Golang 实现,使用 Gin 框架处理图片上传,然后进行裁剪和添加水印的功能。

功能概述

  1. 接收图片上传(支持 JPG/PNG)
  2. 对图片进行裁剪(可指定尺寸或保持比例)
  3. 添加文字或图片水印
  4. 保存处理后的图片

代码实现

package main

import (
	"fmt"
	"image"
	"image/draw"
	"image/jpeg"
	"image/png"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/disintegration/imaging"
	"github.com/gin-gonic/gin"
	"github.com/golang/freetype"
	"github.com/golang/freetype/truetype"
	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"
)

const (
	uploadDir      = "./uploads"
	processedDir   = "./processed"
	maxUploadSize  = 10 << 20 // 10MB
	defaultQuality = 85
)

func init() {
	// 创建必要的目录
	if err := os.MkdirAll(uploadDir, 0755); err != nil {
		log.Fatal(err)
	}
	if err := os.MkdirAll(processedDir, 0755); err != nil {
		log.Fatal(err)
	}
}

func main() {
	r := gin.Default()

	// 设置路由
	r.POST("/upload", uploadHandler)
	r.GET("/image/:filename", getImageHandler)

	// 启动服务器
	fmt.Println("Server started on :8080")
	if err := r.Run(":8080"); err != nil {
		log.Fatal(err)
	}
}

// 上传处理函数
func uploadHandler(c *gin.Context) {
	// 限制上传文件大小
	c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, maxUploadSize)
	if err := c.Request.ParseMultipartForm(maxUploadSize); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "文件太大,最大支持10MB"})
		return
	}

	// 获取上传的文件
	file, header, err := c.Request.FormFile("image")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "无效的文件"})
		return
	}
	defer file.Close()

	// 检查文件类型
	ext := strings.ToLower(filepath.Ext(header.Filename))
	if ext != ".jpg" && ext != ".jpeg" && ext != ".png" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "只支持JPG/PNG格式图片"})
		return
	}

	// 生成唯一文件名
	timestamp := time.Now().Unix()
	originalFilename := fmt.Sprintf("%d_original%s", timestamp, ext)
	processedFilename := fmt.Sprintf("%d_processed%s", timestamp, ext)

	// 保存原始文件
	originalPath := filepath.Join(uploadDir, originalFilename)
	outFile, err := os.Create(originalPath)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "无法保存文件"})
		return
	}
	defer outFile.Close()

	if _, err := io.Copy(outFile, file); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "无法保存文件"})
		return
	}

	// 处理图片(裁剪和加水印)
	processedPath := filepath.Join(processedDir, processedFilename)
	if err := processImage(originalPath, processedPath); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "图片处理失败", "details": err.Error()})
		return
	}

	// 返回响应
	c.JSON(http.StatusOK, gin.H{
		"message":        "图片上传并处理成功",
		"original_image": originalFilename,
		"processed_image": processedFilename,
	})
}

// 获取图片处理函数
func getImageHandler(c *gin.Context) {
	filename := c.Param("filename")
	processedPath := filepath.Join(processedDir, filename)

	// 检查文件是否存在
	if _, err := os.Stat(processedPath); os.IsNotExist(err) {
		c.JSON(http.StatusNotFound, gin.H{"error": "图片未找到"})
		return
	}

	// 提供图片文件
	c.File(processedPath)
}

// 图片处理函数(裁剪+水印)
func processImage(inputPath, outputPath string) error {
	// 打开原始图片
	img, err := imaging.Open(inputPath)
	if err != nil {
		return fmt.Errorf("打开图片失败: %v", err)
	}

	// 1. 裁剪图片(示例:裁剪为300x300,从中心开始)
	img = imaging.Fill(img, 300, 300, imaging.Center, imaging.Lanczos)

	// 2. 添加文字水印
	img, err = addTextWatermark(img, "Watermark Text")
	if err != nil {
		return fmt.Errorf("添加文字水印失败: %v", err)
	}

	// 3. 保存处理后的图片
	ext := strings.ToLower(filepath.Ext(outputPath))
	switch ext {
	case ".jpg", ".jpeg":
		err = imaging.Save(img, outputPath, imaging.JPEGQuality(defaultQuality))
	case ".png":
		err = imaging.Save(img, outputPath)
	default:
		return fmt.Errorf("不支持的输出格式")
	}

	if err != nil {
		return fmt.Errorf("保存图片失败: %v", err)
	}

	return nil
}

// 添加文字水印
func addTextWatermark(img image.Image, text string) (*image.NRGBA, error) {
	// 加载字体(这里使用默认字体,实际项目中可以加载自己的字体文件)
	fontBytes := MustAsset("assets/fonts/Roboto-Regular.ttf") // 示例,需要提供字体文件
	f, err := freetype.ParseFont(fontBytes)
	if err != nil {
		return nil, fmt.Errorf("解析字体失败: %v", err)
	}

	// 创建新的图像,复制原始图像
	bounds := img.Bounds()
	watermarked := image.NewNRGBA(bounds)
	draw.Draw(watermarked, bounds, img, image.Point{}, draw.Src)

	// 设置水印参数
	c := freetype.NewContext()
	c.SetDPI(72)
	c.SetFont(f)
	c.SetFontSize(24)
	c.SetClip(bounds)
	c.SetDst(watermarked)
	c.SetSrc(image.White) // 水印颜色

	// 计算水印位置(右下角)
	pt := freetype.Pt(bounds.Dx()-200, bounds.Dy()-20)

	// 绘制水印
	if _, err := c.DrawString(text, pt); err != nil {
		return nil, fmt.Errorf("绘制水印失败: %v", err)
	}

	return watermarked, nil
}

// 添加图片水印
func addImageWatermark(background, watermark image.Image, opacity float64) (*image.NRGBA, error) {
	bgBounds := background.Bounds()
	wmBounds := watermark.Bounds()

	// 创建新的图像,复制原始图像
	watermarked := image.NewNRGBA(bgBounds)
	draw.Draw(watermarked, bgBounds, background, image.Point{}, draw.Src)

	// 计算水印位置(右下角)
	offset := image.Pt(
		bgBounds.Dx()-wmBounds.Dx()-10,
		bgBounds.Dy()-wmBounds.Dy()-10,
	)

	// 应用水印(带透明度)
	draw.DrawMask(
		watermarked,
		wmBounds.Add(offset),
		watermark,
		image.Point{},
		&image.Uniform{image.Alpha{uint8(opacity * 255)}},
		image.Point{},
		draw.Over,
	)

	return watermarked, nil
}

// MustAsset 模拟加载字体文件(实际项目中应该从文件系统加载)
func MustAsset(path string) []byte {
	// 这里应该从文件系统加载字体文件
	// 示例中使用空字节,实际项目需要替换
	return []byte{}
}

依赖说明

  1. 使用了 Gin 框架处理 HTTP 请求
  2. 使用 disintegration/imaging 库进行图片处理
  3. 使用 golang/freetype 添加文字水印

安装依赖

go get -u github.com/gin-gonic/gin
go get -u github.com/disintegration/imaging
go get -u github.com/golang/freetype

使用说明

  1. 启动服务器: go run main.go
  2. 使用 POST 请求上传图片到 /upload 端点表单字段名: image
  3. 处理后的图片会自动进行裁剪和添加水印
  4. 使用 GET 请求访问 /image/:filename 获取处理后的图片

扩展建议

  1. 添加更多图片处理选项(旋转、滤镜等)
  2. 支持配置水印样式(位置、大小、颜色等)
  3. 添加用户认证
  4. 实现图片元数据存储
  5. 支持批量处理
  6. 添加图片压缩功能
  7. 实现分布式处理

注意事项

  1. 文字水印需要提供字体文件(示例中的 MustAsset 函数需要实现)
  2. 图片水印功能需要提供水印图片
  3. 生产环境需要考虑安全性(文件类型检查、大小限制等)


最后

以上就是名字长了才好记最近收集整理的关于golang实现一个图片上传上传裁剪并加水印功能的全部内容,更多相关golang实现一个图片上传上传裁剪并加水印功能内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(96)

评论列表共有 0 条评论

立即
投稿
返回
顶部