此模块用于处理最后一次的渲染结果并提供预览图片在web端显示,它提供的接口如下:
type LastRendered interface {
// QueueImage queues an image for processing. Returns
// `last_rendered.ErrQueueFull` if there is no more space in the queue for
// new images.
QueueImage(payload last_rendered.Payload) error
// PathForJob returns the base path for this job's last-rendered images.
PathForJob(jobUUID string) string
// ThumbSpecs returns the thumbnail specifications.
ThumbSpecs() []last_rendered.Thumbspec
// JobHasImage returns true only if the job actually has a last-rendered image.
JobHasImage(jobUUID string) bool
}
image_processing
使用go语言中的image库来处理图片,包括图片的解码,压缩,保存。
last_rendered
last_rendered中的数据结构定义如下:
type Storage interface {
// ForJob returns the directory path for storing job-related files.
ForJob(jobUUID string) string
}
// LastRenderedProcessor processes "last-rendered" images and stores them with
// the job.
type LastRenderedProcessor struct {
storage Storage
// TODO: expand this queue to be per job, so that one spammy job doesn't block
// the queue for other jobs.
queue chan Payload
}
// Payload contains the actual image to process.
type Payload struct {
JobUUID string // Used to determine the directory to store the image.
WorkerUUID string // Just for logging.
MimeType string
Image []byte
// Callback is called when the image processing is finished.
Callback func(ctx context.Context)
}
// Thumbspec specifies a thumbnail size & filename.
type Thumbspec struct {
Filename string
MaxWidth int
MaxHeight int
}
每个数据结构的功能已经注释已经介绍清楚了。需要处理的图片信息保存到一个Payload 中,LastRenderedProcessor 有一个Payload 队列,它会循环处理队列中的每一张图片。
看一下四个接口的实现:
// 将一个Payload加入到LastRenderedProcessor的队列中
// QueueImage queues an image for processing.
// Returns `ErrQueueFull` if there is no more space in the queue for new images.
func (lrp *LastRenderedProcessor) QueueImage(payload Payload) error {
logger := payload.sublogger(log.Logger)
select {
case lrp.queue <- payload:
logger.Debug().Msg("last-rendered: queued image for processing")
return nil
default:
logger.Debug().Msg("last-rendered: unable to queue image for processing")
return ErrQueueFull
}
}
// 简单的storage功能包装,输入一个job的uuid,输出它的last-rendered images
// PathForJob returns the base path for this job's last-rendered images.
func (lrp *LastRenderedProcessor) PathForJob(jobUUID string) string {
return lrp.storage.ForJob(jobUUID)
}
// 判断job是否有last-rendered image
// JobHasImage returns true only if the job actually has a last-rendered image.
// Only the lowest-resolution image is tested for. Since images are processed in
// order, existence of the last one should imply existence of all of them.
func (lrp *LastRenderedProcessor) JobHasImage(jobUUID string) bool {
dirPath := lrp.PathForJob(jobUUID)
filename := thumbnails[len(thumbnails)-1].Filename
path := filepath.Join(dirPath, filename)
_, err := os.Stat(path)
switch {
case err == nil:
return true
case errors.Is(err, fs.ErrNotExist):
return false
default:
log.Warn().Err(err).Str("path", path).Msg("last-rendered: unexpected error checking file for existence")
return false
}
}
// 获得缩略图的配置
thumbnails = []Thumbspec{
{"last-rendered.jpg", 1920, 1080},
{"last-rendered-small.jpg", 600, 338},
{"last-rendered-tiny.jpg", 200, 112},
}
// ThumbSpecs returns the thumbnail specifications.
func (lrp *LastRenderedProcessor) ThumbSpecs() []Thumbspec {
// Return a copy so modification of the returned slice won't affect the global
// `thumbnails` variable.
copied := make([]Thumbspec, len(thumbnails))
copy(copied, thumbnails)
return copied
}
处理图片的核心函数:
// processImage down-scales the image to a few thumbnails for presentation in
// the web interface, and stores those in a job-specific directory.
//
// Because this is intended as internal queue-processing function, errors are
// logged but not returned.
func (lrp *LastRenderedProcessor) processImage(ctx context.Context, payload Payload) {
jobDir := lrp.PathForJob(payload.JobUUID)
logger := log.With().Str("jobDir", jobDir).Logger()
logger = payload.sublogger(logger)
// Decode the image.
image, err := decodeImage(payload)
if err != nil {
logger.Error().Err(err).Msg("last-rendered: unable to decode image")
return
}
// Generate the thumbnails.
for _, spec := range thumbnails {
thumbLogger := spec.sublogger(logger)
thumbLogger.Trace().Msg("last-rendered: creating thumbnail")
// 将图片缩小
image = downscaleImage(spec, image)
// 保存在对应的job下面
imgpath := filepath.Join(jobDir, spec.Filename)
if err := saveJPEG(imgpath, image); err != nil {
thumbLogger.Error().Err(err).Msg("last-rendered: error saving thumbnail")
break
}
}
// Call the callback, if provided.
if payload.Callback != nil {
payload.Callback(ctx)
}
}
总结
last_rendered是一个工具模块,用于将渲染出的图片生成预览图,这样渲染完成后就可以在web端直接看到渲染结果,总体上来说是一个为了程序的易用性设计的模块。