从存储桶将多个文件传递给exec.Command调用
I'm attempting to build a cloud function written in Go, that will use the available ImageMagick library in Google's Cloud Functions infrastructure to composite & manipulate several images into a resulting output image.
The root of the problem, is that the ImageMagick function that I want to use is available, but it takes multiple distinct inputs in order to work. My inputs are objects in a Storage Bucket.
The os/exec Cmd struct allows you to do this, by using an "ExtraFiles" array, and I know how to provide these extra files to my ImageMagick command.
However, the "ExtraFiles" array only wants to store instances of os.File, whereas the GCP Storage Client gives you a "Reader" instance when you open a file.
backgroundBlob := storageClient.Bucket(inputBucket).Object(background)
backgroundImg, err := backgroundBlob.NewReader(ctx)
if err != nil {
return "", fmt.Errorf("backgroundImg: %v", err)
}
foregroundBlob := storageClient.Bucket(inputBucket).Object(foreground)
foregroundImg, err := foregroundBlob.NewReader(ctx)
if err != nil {
return "", fmt.Errorf("foregroundImg: %v", err)
}
outputBlob := storageClient.Bucket(outputBucket).Object(output)
outputImg := outputBlob.NewWriter(ctx)
defer outputImg.Close()
// Set up some additional file handles since we're dealing with more inputs than STDIN Can cope with
cmd := exec.Command("convert", "fd:3", "fd:4", "-composite", "fd:5")
cmd.ExtraFiles = append(cmd.ExtraFiles,backgroundImg)
cmd.ExtraFiles = append(cmd.ExtraFiles,foregroundImg)
cmd.ExtraFiles = append(cmd.ExtraFiles,outputImg)
if err := cmd.Run(); err != nil {
return "", fmt.Errorf("cmd.Run: %v", err)
}
log.Printf("Blurred image has been uploaded to %s", outputBlob.ObjectName())
return outputBlob.ObjectName(), nil
}```
So, from where I am at the moment, I need to do one of the following two things:
1. Figure out how to get Google's storage API to treat/use objects in their storage buckets as "os.File"s
OR
2. Figure out how to execute my "convert" command using multiple Readers as input, rather than leveraging the ExtraFiles array.
If anybody out there has any ideas on how to achieve either of the above, or has alternate ways of solving this problem, I would be very grateful to hear them!
To anybody who happens across this question in the future - I found a workaround. I'm not thrilled with it, but it is functional.
Essentially, I was able to slurp the contents of the storage Readers & then create my own os.File objects within the Cloud Functions context. Then, passing that os.File to the exec.Cmd invocation. It's definitely not ideal, but here's the code:
func Overlay(ctx context.Context, inputBucket, outputBucket, background string, foreground string, output string) (string, error) {
// Retrieve the Background Image from the storage API
backgroundBlob := storageClient.Bucket(inputBucket).Object(background)
backgroundImg, err := backgroundBlob.NewReader(ctx)
if err != nil {
return "", fmt.Errorf("backgroundImg: %v", err)
}
// Read the contents of the file into a variable
backgroundSlurp, err := ioutil.ReadAll(backgroundImg)
if err != nil {
return "", fmt.Errorf("readFile: unable to read data from bucket %q, file %q: %v", inputBucket, background, err)
}
// Write the contents of Background Image to an os.File instance
err = ioutil.WriteFile(fmt.Sprintf("/tmp/%s",background), backgroundSlurp, 0644)
if err != nil {
return "", fmt.Errorf("backgroundImg: %v", err)
}
// Open the os.File instance to pass it on to os.exec later
backgroundFile, err := os.Open(fmt.Sprintf("/tmp/%s",background))
if err != nil {
return "", fmt.Errorf("backgroundFile: %v", err)
}
foregroundBlob := storageClient.Bucket(inputBucket).Object(foreground)
foregroundImg, err := foregroundBlob.NewReader(ctx)
if err != nil {
return "", fmt.Errorf("foregroundImg: %v", err)
}
// Read the contents of the file into a variable
foreroundSlurp, err := ioutil.ReadAll(foregroundImg)
if err != nil {
return "", fmt.Errorf("readFile: unable to read data from bucket %q, file %q: %v", inputBucket, foreground, err)
}
// Write the contents of Foreground Image to an os.File instance
err = ioutil.WriteFile(fmt.Sprintf("/tmp/%s",foreground), foreroundSlurp, 0644)
if err != nil {
return "", fmt.Errorf("foregroundImg: %v", err)
}
// Open the os.File instance to pass it on to os.exec later
foregroundFile, err := os.Open(fmt.Sprintf("/tmp/%s",foreground))
if err != nil {
return "", fmt.Errorf("foregroundFile: %v", err)
}
outputBlob := storageClient.Bucket(outputBucket).Object(output)
outputImg := outputBlob.NewWriter(ctx)
defer outputImg.Close()
// Set up some additional file handles since we're delaqing with more inputs than STDIN Can cope with
cmd := exec.Command("convert", "fd:3", "fd:4", "-composite", "-")
cmd.Stdout = outputImg
cmd.ExtraFiles = append(cmd.ExtraFiles,backgroundFile)
cmd.ExtraFiles = append(cmd.ExtraFiles,foregroundFile)
if err := cmd.Run(); err != nil {
return "", fmt.Errorf("cmd.Run: %v", err)
}
log.Printf("Blurred image has been uploaded to %s", outputBlob.ObjectName())
return outputBlob.ObjectName(), nil
}