Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
[Unity] Build Asset Bundle, and upload to AWS-S3
using UnityEngine;
using UnityEngine.Networking;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System;
// Usage
// 1. Put this script to Assets/Editor/
// 2. Execute [Menu] -> [Build] -> [Build Asset Bundle]
public class UnityAssetBundleBuilder {
// Enter your s3 informations
private const string s3Bucket = "";
private const string s3KeyBase = "";
private const string s3AccessKeyId = "";
private const string s3SecretAccessKey = "";
private const string baseDir = "AssetBundles";
private const string s3PostUrl = "http://" + s3Bucket + ".s3.amazonaws.com/";
private static readonly BuildTarget[] buildTargets =
new BuildTarget[] { BuildTarget.iOS, BuildTarget.Android };
private static readonly Dictionary<string, string> postParams = new Dictionary<string, string> {
{ "acl", "public-read" },
{ "Content-Type", "application/octet-stream" },
{ "x-amz-meta-uuid", "14365123651274" },
{ "AWSAccessKeyId", s3AccessKeyId },
};
private const string policyBase = "{{" +
"\"expiration\": \"{0}\"," +
"\"conditions\": [" +
" {{\"bucket\": \"" + s3Bucket + "\"}}," +
" [\"starts-with\", \"$key\", \"{1}\"]," +
" {{\"acl\": \"public-read\"}}," +
" [\"starts-with\", \"$Content-Type\", \"application/octet-stream\"]," +
" {{\"x-amz-meta-uuid\": \"14365123651274\"}}" +
" ]" +
"}}";
[MenuItem("Build/Build AssetBundles")]
public static void Build() {
if (Directory.Exists(baseDir)) {
Directory.Delete(baseDir, true);
}
Directory.CreateDirectory(baseDir);
foreach (BuildTarget target in buildTargets) {
buildForPlatform(target);
}
}
private static void buildForPlatform(BuildTarget target) {
var platform = target.ToString().ToLower();
string dir = Path.Combine(baseDir, platform);
Directory.CreateDirectory(dir);
BuildPipeline.BuildAssetBundles(dir,
BuildAssetBundleOptions.ChunkBasedCompression,
target);
var dirInfo = new DirectoryInfo(dir);
foreach (var fileInfo in dirInfo.GetFiles()) {
Debug.Log("upload : " + fileInfo.FullName);
uploadToS3(platform, fileInfo);
}
}
private static void uploadToS3(string platform, FileInfo fileInfo) {
var form = new WWWForm();
foreach (KeyValuePair<string, string> pair in postParams) {
form.AddField(pair.Key, pair.Value);
}
string s3Key = Path.Combine(s3KeyBase, platform + "/" + fileInfo.Name);
form.AddField("key", s3Key);
string expiration = DateTime.Now.AddMinutes(1).ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
string policy = string.Format(policyBase, expiration, s3Key);
string base64Policy = Convert.ToBase64String(Encoding.UTF8.GetBytes(policy));
var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(s3SecretAccessKey));
string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(base64Policy)));
form.AddField("Policy", base64Policy);
form.AddField("Signature", signature);
byte[] fileBytes = File.ReadAllBytes(fileInfo.FullName);
form.AddBinaryData("file", fileBytes, fileInfo.Name, "application/octet-stream");
if (!postRequestWithRetry(s3Key, form)) {
Debug.LogError("Upload failed : " + s3Key);
}
}
private static bool postRequestWithRetry(string s3Key, WWWForm form, int retryLimit = 5) {
for (int i = 0; i < retryLimit; i++) {
if (postRequest(s3Key, form)) {
return true;
}
Debug.Log("Retry : " + (i + 1));
}
return false;
}
private static bool postRequest(string s3Key, WWWForm form) {
using (UnityWebRequest www = UnityWebRequest.Post(s3PostUrl, form)) {
var s = www.Send();
long startTick = DateTime.Now.Ticks;
while (!s.isDone) {
if (DateTime.Now.Ticks > startTick + 10L * 10000000L) {
Debug.LogWarning("Timeout");
break;
}
}
if (string.IsNullOrEmpty(www.error)) {
Debug.Log("Uploaded : " + s3PostUrl + s3Key);
return true;
} else {
Debug.LogWarning("Error : " + www.error);
return false;
}
}
}
}
@e-nathan

This comment has been minimized.

Copy link

@e-nathan e-nathan commented Apr 24, 2018

@tkyaji I used this script but Im getting this error "Generic/unknown HTTP error unity"

So i tried calling the url from Postman , and Im getting the following error The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256.

My s3 Bucket is in the following region Asia Pacific Mumbai

screenshot 29

Can you please help me out? I was also a bit confused regarding s3KeyBase , is the value available from aws console?

@MattLP

This comment has been minimized.

Copy link

@MattLP MattLP commented Aug 6, 2018

@e-nathan I had the same issue, then I manage to see the real error, use www.downloadhandler.text, if it's a failure, the text property will contains the specific error from Amazon.

@jackbrookes

This comment has been minimized.

Copy link

@jackbrookes jackbrookes commented Aug 6, 2018

@MattLP I understand the issue is to do with newer authentication methods. Can you suggest changes to update the script?

@MattLP

This comment has been minimized.

Copy link

@MattLP MattLP commented Aug 7, 2018

@jackbrookes Actually HMACSHA1 worked perfectly for me, the issue that blocked me for a bit was the folder settings. Our folder here is private, so when I was using
" {{\"acl\": \"public-read\"}}," + it just didn't work, I had to use " {{\"acl\": \"private\"}}," + instead and it worked like a charm.

Also, DateTime.now didn't fit with S3, I had to use UTCNow.

@nergulnergul

This comment has been minimized.

Copy link

@nergulnergul nergulnergul commented Jun 21, 2019

Hi all, I would like to know what the s3KeyBase is for.

I am having trouble to get 400 bad request, I fulfilled the requirement and apply above comments but still doesn't work

Can anyone help me?
Thank you

@JulioNectar

This comment has been minimized.

Copy link

@JulioNectar JulioNectar commented Mar 29, 2020

You can please tell me where i find the s3KeyBase?

// Enter your s3 informations
private const string s3Bucket = "";
private const string s3KeyBase = "";
private const string s3AccessKeyId = "";
private const string s3SecretAccessKey = "";

@12xq4

This comment has been minimized.

Copy link

@12xq4 12xq4 commented Jun 16, 2020

You can please tell me where i find the s3KeyBase?

// Enter your s3 informations
private const string s3Bucket = "";
private const string s3KeyBase = "";
private const string s3AccessKeyId = "";
private const string s3SecretAccessKey = "";

Based on cross referencing other source codes, since the "key" in AddField("key", s3Key) implies the name that you'd insert for the file you're uploading onto s3, it would seem that s3KeyBase is any root name that you would want your item to retain on the s3 bucket. For example, if your bucket has some kind of "folder" structure like Project1/Username/platform/filename, then your s3Keybase in this case would be Project1/Username/

@SanilShah

This comment has been minimized.

Copy link

@SanilShah SanilShah commented Jul 28, 2020

Getting 400 bad request.

@c-andrews

This comment has been minimized.

Copy link

@c-andrews c-andrews commented Nov 18, 2020

Can you tell me if this script can be used as a Post Process Build Script in Unity Cloud Build?
If so is the baseDir going to be different?

In this doc: https://docs.unity3d.com/Manual/UnityCloudBuildAddressables.html
It says:

The Addressable content for your build is located at $WORKSPACE/.build/last/<BUILD_TARGET_ID>/extra_data/addrs/

Can I access the $WORKSPACE variable to get the root path?

Thanks in advance

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