Skip to content

Instantly share code, notes, and snippets.

@kyokomi
Last active February 14, 2018 14:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kyokomi/5a500e88e9be55a4e694f30759d0aa76 to your computer and use it in GitHub Desktop.
Save kyokomi/5a500e88e9be55a4e694f30759d0aa76 to your computer and use it in GitHub Desktop.
Authenticating Requests in Browser-Based Uploads Using POST (AWS Signature Version 2) for golang http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/UsingHTTPPOST.html
package main
import (
"flag"
"fmt"
"log"
"os"
"io/ioutil"
"mime/multipart"
"github.com/k0kubun/pp"
"github.com/pborman/uuid"
"bytes"
"io"
"net/http"
)
func postFile(filename string, url string, formData map[string]string) error {
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
for k, v := range formData {
bodyWriter.WriteField(k, v)
}
fileWriter, err := bodyWriter.CreateFormFile("file", filename)
if err != nil {
return err
}
fh, err := os.Open(filename)
if err != nil {
return err
}
defer fh.Close()
if _, err = io.Copy(fileWriter, fh); err != nil {
return err
}
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()
resp, err := http.Post(url, contentType, bodyBuf)
if err != nil {
return err
}
defer resp.Body.Close()
resp_body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
fmt.Println(resp.Status)
fmt.Println(string(resp_body))
return nil
}
func main() {
awsAccessKeyID := flag.String("aid", "", "AWS_ACCESS_KEY_ID")
awsSecretKeyID := flag.String("sid", "", "AWS_SECRET_KEY_ID")
s3BucketName := flag.String("b", "", "S3_BUCKET_NAME")
filePath := flag.String("f", "", "Upload file path")
flag.Parse()
f, err := os.Open(filePath)
if err != nil {
log.Fatalln(err)
}
fileID := uuid.New()
userID := 111111 // TODO:
// input data
fi, err := f.Stat()
if err != nil {
log.Fatalln(err)
}
uploadObjectKey := fmt.Sprintf("files/%d/%s.%s", userID, fileID, "png")
policies, err := CreatePolicies(AWSCredentials{
AWSAccessKeyID: awsAccessKeyID,
AWSSecretKeyID: awsSecretKeyID,
}, UploadConfig{
BucketName: s3BucketName,
FileSize: fi.Size(),
ContentType: "image/png", // TODO:
ObjectKey: uploadObjectKey,
})
if err != nil {
log.Fatalln(err)
}
pp.Print(policies)
if err := postFile(filePath, policies.URL, policies.Form); err != nil {
log.Fatalln(err)
}
}
package main
import (
"encoding/base64"
"crypto/hmac"
"crypto/sha1"
"fmt"
"time"
"encoding/json"
"strings"
)
// AWSCredentials Amazon Credentials
type AWSCredentials struct {
AWSSecretKeyID string
AWSAccessKeyID string
}
// UploadConfig generate policies from config
type UploadConfig struct {
BucketName string
ObjectKey string
ContentType string
FileSize int64
}
// UploadPolicies Amazon s3 upload policies
type UploadPolicies struct {
URL string
Form map[string]string
}
// PolicyJSON is policy rule
type PolicyJSON struct {
Expiration string `json:"expiration"`
Conditions []interface{} `json:"conditions"`
}
const expirationTimeFormat = "2006-01-02T15:04:05ZZ07:00"
const expirationHour = 1 * time.Hour
const uploadURLFormat = "http://%s.s3.amazonaws.com/" // <bucketName>
// CreatePolicies create amazon s3 to upload policies return
func CreatePolicies(awsCredentials AWSCredentials, fileInfo UploadConfig) (UploadPolicies, error) {
data, err := json.Marshal(&PolicyJSON{
Expiration: time.Now().Add(expirationHour).Format(expirationTimeFormat),
Conditions: []interface{}{
map[string]string{"bucket": fileInfo.BucketName},
map[string]string{"key": fileInfo.ObjectKey},
map[string]string{"Content-Type": fileInfo.ContentType},
[]interface{}{"content-length-range", fileInfo.FileSize, fileInfo.FileSize},
},
})
if err != nil {
return UploadPolicies{}, err
}
policy := strings.Replace(base64.StdEncoding.EncodeToString(data), "\n", "", -1)
mac := hmac.New(sha1.New, []byte(awsCredentials.AWSSecretKeyID))
mac.Write([]byte(policy))
expectedMAC := mac.Sum(nil)
signature := strings.Replace(base64.StdEncoding.EncodeToString(expectedMAC), "\n", "", -1)
uploadURL := fmt.Sprintf(uploadURLFormat, fileInfo.BucketName)
return UploadPolicies{
URL: uploadURL,
Form: map[string]string{
"AWSAccessKeyId": awsCredentials.AWSAccessKeyID,
"key": fileInfo.ObjectKey,
"Content-Type": fileInfo.ContentType,
"signature": signature,
"policy": policy,
},
}, nil
}
@kyokomi
Copy link
Author

kyokomi commented Feb 14, 2018

v4も対応したので、このライブラリを使った方が良い
https://github.com/kyokomi/s3gopolicy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment