Mads Moi-Aune
Golang: Azure Functions Blob Storage Output Binding
Lately, I’ve been setting up an Azure Function App with a custom handler written in Go. One of my functions needs to download a file from an external URL and then upload that file to Azure Blob Storage. Unfortunately, neither the documentation on Microsoft Learn or the examples on Github mentions how to use Blob Storage as output binding for custom handlers. So I decided to do a little write up on how I solved it.
There are two types of files that you can upload:
- binary
- textfile
If you are uploading a binary the Azure Function App host expects the file as byte array ([]byte
) otherwise it expects the file as base64 encoded string.
NOTE: I dont handle errors in these examples to keep the code short, but you should always handle errors!
As binary
To upload a file a binary to Blob Storage using output bindings we need to specify in the function.json
that the dataType
will be ‘binary’. Then when we return our custom handler payload to Azure Function App, the returnValue
must be a byte array ([]byte
).
// file: function.json
{
"bindings": [
// ...
{
"name": "$return",
"type": "blob",
"direction": "out",
"path": "reports/my_report.csv",
"connection": "AzureWebJobsStorage",
"dataType": "binary"
}
]
}
// file: handler.go
type BlobOutputBinding struct {
ReturnValue interface{}
}
func DownloadHandler(w http.ResponseWriter, r *http.Request) {
// ... Logic for downloading file
// 1. Convert our response body (a.k.a downloaded file) to []byte
data, _ := io.ReadAll(resp.Body)
// 2. Create our custom handler response payload
// since we named our blob output `$return`, we can use the `returnValue` instead of `Outputs["outblob"]`
binding := BlobOutputBinding{
ReturnValue: data
}
// 3. convert the binding to JSON
response, _ := json.Marshal(binding)
// 4. Respond to Azure Function with out binding
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(response)
}
As text file
If we want to upload a text file the dataType
in function.json
must be ‘string’, or we can leave it out because ‘string’ is the default value. When dataType
is ‘string’ the Azure Function App expects our custom handler to return the file as a base64 encoded string.
// file: function.json
{
"bindings": [
// ...
{
"name": "$return",
"type": "blob",
"direction": "out",
"path": "reports/my_report.csv",
"connection": "AzureWebJobsStorage",
}
]
}
// file: handler.go
type BlobOutputBinding struct {
ReturnValue interface{}
}
func DownloadHandler(w http.ResponseWriter, r *http.Request) {
// ... logic for downloading file
// 1. convert our response body (a.k.a downloaded file) to []byte
data, _ := io.ReadAll(resp.Body)
// 2. convert to base64
encoded := base64.StdEncoding.EncodeToString(data)
// 3. create our custom handler response payload
// since we named our blob output `$return`, we can use the `returnValue` instead of `Outputs["outblob"]`
binding := BlobOutputBinding{
ReturnValue: encoded
}
// 3. convert the binding to JSON
response, _ := json.Marshal(binding)
// 4. respond to Azure Function with out binding
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(response)
}