需求背景:需要在访问临时链中添加缩略图参数支持,让含有缩略图参数的请求返回指定图片宽高大小的文件,满足编辑器、文件列表中图片资源快速加载的需求。为了每个业务能够快速接入使用、复用,我们选择不在业务服务中进行处理,而是在对象存储服务中进行通用处理。

处理流程:

让缩略图参数不参与签名计算 -> 识别图片资源请求 -> 处理文件流,按照指定宽高裁剪 -> 返回文件流

安装缩略图处理插件

go get -u github.com/disintegration/imaging

让缩略图参数不参与签名计算

signature-v4.go doesPresignedSignatureMatch()

	notSignParams := set.CreateStringSet(
		"filename",
		"imageWidth",
		"imageHeight",
	)
	//  BillYu 去除URL参数中filename参与校验,query参数中不添加filename。
	// Add missing query parameters if any provided in the request URL
	for k, v := range req.Form {
		if !defaultSigParams.Contains(k) && !notSignParams.Contains(k) {
			query[k] = v
		}
	}

处理图片资源

object-handlers.go getObjectHandler()

对比源码原方法是直接把资源的文件流写入到请求响应中,我们需要处理的是缩略图请求,将处理后的图片资源写入请求响应。如果文件使用缩略图处理报错则返回正常的文件资源,不影响正常业务功能。
image.png

	//是图片文件
	objectLowerName := strings.ToLower(object)
	// if filename == "" && (strings.HasSuffix(objectLowerName, "jpg") || strings.HasSuffix(objectLowerName, "png") || strings.HasSuffix(objectLowerName, "jpeg") || strings.HasSuffix(objectLowerName, "bmp")) {
	if strings.HasSuffix(objectLowerName, "jpg") || strings.HasSuffix(objectLowerName, "png") || strings.HasSuffix(objectLowerName, "jpeg") || strings.HasSuffix(objectLowerName, "bmp") {

		imageWidth := GetUrlArg(r, "imageWidth")
		imageHeight := GetUrlArg(r, "imageHeight")
		//存在图片缩略参数
		if imageWidth != "" || imageHeight != "" {
			var updateImage image.Image
			img, format, decodeErr := image.Decode(bufio.NewReader(gr.Reader))
			if decodeErr != nil {
				logger.Error(time.Now().Format("2006-01-02 15:04:05") + " format:" + format + " error:" + decodeErr.Error())
			} else {
				if imageWidth != "" {
					//设置宽高
					width, _ := strconv.Atoi(imageWidth)
					var height int = 0
					if imageHeight != "" {
						height, _ = strconv.Atoi(imageHeight)
					}
					updateImage = imaging.Resize(img, width, height, imaging.Lanczos)
				} else if imageHeight != "" {
					//设置高度
					var height int = 0
					height, _ = strconv.Atoi(imageHeight)
					updateImage = imaging.Resize(img, 0, height, imaging.Lanczos)
				}
				w.Header().Set(xhttp.ContentType, "image/jpeg")
				w.Header().Del(xhttp.ContentLength)
				if encodeError := jpeg.Encode(httpWriter, updateImage, nil); encodeError != nil {
					if !httpWriter.HasWritten() && !statusCodeWritten {
						// write error response only if no data or headers has been written to client yet
						writeErrorResponse(ctx, w, toAPIError(ctx, encodeError), r.URL)
						return
					}
					if !xnet.IsNetworkOrHostDown(encodeError, true) { // do not need to log disconnected clients
						logger.LogIf(ctx, fmt.Errorf("Unable to write all the data to client %w", encodeError))
					}
					return
				}
				processImage = true
			}
		}
	}
	if !processImage {
		// Write object content to response body
		if _, err = xioutil.Copy(httpWriter, gr); err != nil {
			if !httpWriter.HasWritten() && !statusCodeWritten {
				// write error response only if no data or headers has been written to client yet
				writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
				return
			}
			if !xnet.IsNetworkOrHostDown(err, true) { // do not need to log disconnected clients
				logger.LogIf(ctx, fmt.Errorf("Unable to write all the data to client %w", err))
			}
			return
		}
	}

responseHeader统一处理

其中需要特别注意的问题是对responseHeader的处理,源码中通过文件属性对responseHeader进行统一设置,这样每次返回的content-length都是固定的,但是我们对图片进行了缩略处理,导致文件大小发生了变化。这样即使返回的是缩略图资源,但是responseHeader中的content-length与实际资源大小不一致,这样会使浏览器认为资源文件下载有问题发生错误。

所以我们在返回缩略图资源前将content-length值移除,使浏览器能够正常响应。

关于源码中的请求头通用设置:
image.png

image.png

image.png

其中包括:content-type content-length version等存储对象本身的信息。

实现效果:

使用方法
在访问临时链追加参数:宽度&imageWidth= 和 高度&imageHeight=
只设置一个参数时(或一个参数为0)另一个参数按照宽高等比自动填充。

原图:
image.png

缩略图链接
https://demostorage.iflydocs.com/bucket2/storage/6257cd7c-908a-40ba-8114-c9eb9010ac6e.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=KhZaO4%2F20220815%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220815T022404Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=032364507daee8954364c6c5a1b5571f8f5159193f74f4c5315b34b45784197d&imageWidth=200

缩略图:
image.png