Backing up BigCommerce Product Images Using Go!

Nikita Puzanenko
BigCommerce Developer Blog
4 min readAug 22, 2019

Accidentally deleting product images can be a huge bummer. But luckily, backing up your product images is a task that lends itself well to automation saving you a ton of time in the event of an unfortunate mistake.

If you have ever looked at the WebDav “product_images” directory for a BigCommerce store, you’ve most likely noticed that your uploaded images are assigned seemingly random paths.

This means that even if you have all of your product images backed up, if you do not reproduce this exact path, you will need to manually add images to all of your products if they are deleted.

Let’s write a quick script to make this a bit less painful while at the same time brushing up on our Go. The complete code is available here.

Prerequisites

Before you begin, be sure that you have Go installed. If it’s your first time working with Go on your machine, follow the Go Getting Started Guide to install the Go toolset.

Then, create a new project folder in your Go path. For example:

$HOME/go/src/image-backup

Alternatively, you could use Go mod to create the project outside of the prescribed Go path. For more information on Go mod, check out this intro article and the official wiki.

Building the Backup Script

Writing a WebDav client is a bit outside of the scope of this tutorial so we will be using gowebdav. You can get it by running “$ go get github.com/studio-b12/gowebdav” in your terminal.

Now we’ll create a new file for the backup script. In your project folder, create images.go. In the file contents, we’ll define our package name and its required imports.

package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/studio-b12/gowebdav"
)

Let’s set up a config struct to parse and load WebDav credentials. Add the following to your script:

type config struct {
Username string `json:"username"`
Password string `json:"password"`
Root string `json:"root"`
LocalPath string `json:"localPath"`
}

Next, we’ll create a config.json file to hold those values. Create a new file in your project directory called config.json. The contents will be a JSON object containing the values for your WebDAV credentials. For example:

{
"username": "storeowner@email.com",
"password": "234sdfdsoi32452sdfw8098sdfmsdfjsdflk08",
"root": "https://mystore.com/dav",
"localPath": "/Users/mycomputer/backed-up-images"
}

Username and Password can be acquired from the BigCommerce control panel, under Server Settings>WebDAV. Root is just the starting WebDav path ie: “https://storeurl.com/dav”. LocalPath represents the final destination or the backup.

Back in the images.go file, we’ll add a function to read the config.json file and parse its JSON into our config struct.

func loadConfiguration(file string) config {
var config config
configFile, err := os.Open(file)
defer configFile.Close()
if err != nil {
fmt.Println(err.Error())
}
jsonParser := json.NewDecoder(configFile)
jsonParser.Decode(&config)
return config
}

Next we need a function to read directories and return their files.

func getFiles(directory string, client *gowebdav.Client) []os.FileInfo {
files, err := client.ReadDir(directory)
if err != nil {
fmt.Println(err)
}
return files
}

Now we need to read the contents of a WebDav directory and replicate it on our local filesystem. The first thing this function does is create local directories using “os.MkdirAll(currentLocalPath, os.ModePerm)“. Next we loop over each file in the directory and check if the current file is itself a directory using “file.IsDir()”. If this evaluates to true we will recursively call “walkWebdavDir()” again, but on this directory. If the current file is not a directory we will simply copy to our local filesystem (I also added “fmt.Println(localPath)” to keep track of what is happening).

func walkWebdavDir(directory string, currentLocalPath string, client *gowebdav.Client) {
os.MkdirAll(currentLocalPath, os.ModePerm)
for _, file := range getFiles(directory, client) {
webDavSubDirectory := filepath.Join(directory, file.Name())
localPath := filepath.Join(currentLocalPath, file.Name())
if file.IsDir() {
walkWebdavDir(webDavSubDirectory, localPath, client)
} else {
bytes, _ := client.Read(webDavSubDirectory)
ioutil.WriteFile(localPath, bytes, 0644)
fmt.Println(localPath)
}
}
}

Finally we need a main() function to start the whole process.

func main() {
config := loadConfiguration("./config.json")
client := gowebdav.NewClient(config.Root, config.Username, config.Password)
walkWebdavDir("product_images/", config.LocalPath, client)
}

Run the Script

Finally, run `go run images.go` from your project directory to build and run the script and save the image file structure to your local computer. In the event you need to use the backed up files to restore your product images, you can copy the entire directory structure from your local copy to the /product_images/ folder in WebDAV.

Conclusion

Data mishaps can be …unfortunate, but they don’t have to ruin your day if you’re prepared with a backup. In this tutorial, we outlined a basic process for connecting to WebDAV and copying a directory’s contents to your local machine for safekeeping. Feel free to use it as-is, or take it further by incorporating goroutines and channels, or more complete error handling. You could also get fancy and run the script on a timed interval to make sure that your backup is always up to date.

With luck, you’ll never need it. But if you do — you’ll be glad you have a properly-formatted backup, ready to go.

--

--