Skip to content

Instantly share code, notes, and snippets.

@prnthp
Last active August 3, 2021 02:27
Show Gist options
  • Save prnthp/20423d8c2ade8730849766b81ee16552 to your computer and use it in GitHub Desktop.
Save prnthp/20423d8c2ade8730849766b81ee16552 to your computer and use it in GitHub Desktop.
Super simple way to queue up files to be uploaded to your S3 bucket in Unity without relying on the AWS .NET APIs
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;
public class AWSUploadAsync : MonoBehaviour
{
// Adapted from:
// https://medium.com/xrlo-extended-reality-lowdown/uploading-to-aws-from-unity-5e7de2c80fce
// TODO: Point this to your server
private const string awsBucketName = "bucketName";
private const string awsAccessKey = "XXXXXXXXXXXXXXXXXXXX";
private const string awsSecretKey = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
private static string awsURLBaseVirtual = "";
[SerializeField]
private bool disableUploads;
public bool Uploading { get; private set; }
public class Upload
{
public Thread Thread { get; private set; }
public string FileName;
public string FilePath;
public bool Done { get; private set; }
public bool Fail { get; private set; }
private Queue<Upload> _uploads;
public void StartUpload()
{
if (Thread == null)
{
Debug.Log("Starting upload thread");
Thread = new Thread(() => UploadFileToAWS3(FileName, FilePath));
Thread.Start();
}
}
private void UploadFileToAWS3(string fileName, string filePath)
{
Done = false;
Fail = false;
// TODO: You may or may not need this.
// Thread.Sleep(1000);
string currentAWS3Date =
System.DateTime.UtcNow.ToString(
"ddd, dd MMM yyyy HH:mm:ss ") +
"GMT";
string canonicalString =
"PUT\n\n\n\nx-amz-date:" +
currentAWS3Date + "\n/" +
awsBucketName + "/" + fileName;
UTF8Encoding encode = new UTF8Encoding();
HMACSHA1 signature = new HMACSHA1();
signature.Key = encode.GetBytes(awsSecretKey);
byte[] bytes = encode.GetBytes(canonicalString);
byte[] moreBytes = signature.ComputeHash(bytes);
string encodedCanonical = Convert.ToBase64String(moreBytes);
string aws3Header = "AWS " +
awsAccessKey + ":" +
encodedCanonical;
string URL3 = awsURLBaseVirtual + fileName;
var requestS3 = (HttpWebRequest)WebRequest.Create(URL3);
requestS3.Headers.Add("Authorization", aws3Header);
requestS3.Headers.Add("x-amz-date", currentAWS3Date);
byte[] fileRawBytes = File.ReadAllBytes(filePath);
requestS3.ContentLength = fileRawBytes.Length;
requestS3.Method = "PUT";
requestS3.Timeout = 100000;
requestS3.KeepAlive = false;
Debug.Log("[Thread] Starting upload: " + fileName);
try
{
using (var s3Stream = requestS3.GetRequestStream())
{
s3Stream.Write(fileRawBytes, 0, fileRawBytes.Length);
}
Debug.Log("[Thread] Sent bytes: " +
requestS3.ContentLength +
", for file: " +
fileName);
}
catch (Exception e)
{
Debug.Log("[Thread] Upload failed." + e);
Fail = true;
}
requestS3.Abort();
if (Fail)
{
Done = false;
}
else
{
Done = true;
}
}
}
void Start()
{
// TODO: Point this to your AWS S3 bucket's location
awsURLBaseVirtual = "https://" +
awsBucketName +
".s3.us-west-2.amazonaws.com/";
_uploads = new Queue<Upload>();
}
public void UploadFile(string fileName, string filePath)
{
if (disableUploads)
{
return;
}
var upload = new Upload
{
FileName = fileName,
FilePath = filePath
};
_uploads.Enqueue(upload);
}
private void Update()
{
if (_uploads.Count > 0)
{
Uploading = true;
var latestUpload = _uploads.Peek();
if (!latestUpload.Done && latestUpload.Thread == null)
{
Debug.Log("Starting next upload in queue.");
latestUpload.StartUpload();
}
if (latestUpload.Done)
{
Debug.Log("Upload done.");
_uploads.Dequeue();
}
if (latestUpload.Fail)
{
Debug.Log("Upload failed, re-enqueueing");
var oldUpload = _uploads.Dequeue();
var newUpload = new Upload
{
FileName = oldUpload.FileName,
FilePath = oldUpload.FilePath
};
_uploads.Enqueue(newUpload);
}
}
else
{
Uploading = false;
}
}
// Blocking call
private bool UploadFileToAWS3(string fileName, string filePath)
{
string currentAWS3Date =
System.DateTime.UtcNow.ToString(
"ddd, dd MMM yyyy HH:mm:ss ") +
"GMT";
string canonicalString =
"PUT\n\n\n\nx-amz-date:" +
currentAWS3Date + "\n/" +
awsBucketName + "/" + fileName;
UTF8Encoding encode = new UTF8Encoding();
HMACSHA1 signature = new HMACSHA1();
signature.Key = encode.GetBytes(awsSecretKey);
byte[] bytes = encode.GetBytes(canonicalString);
byte[] moreBytes = signature.ComputeHash(bytes);
string encodedCanonical = Convert.ToBase64String(moreBytes);
string aws3Header = "AWS " +
awsAccessKey + ":" +
encodedCanonical;
string URL3 = awsURLBaseVirtual + fileName;
WebRequest requestS3 =
(HttpWebRequest)WebRequest.Create(URL3);
requestS3.Headers.Add("Authorization", aws3Header);
requestS3.Headers.Add("x-amz-date", currentAWS3Date);
byte[] fileRawBytes = File.ReadAllBytes(filePath);
requestS3.ContentLength = fileRawBytes.Length;
requestS3.Method = "PUT";
try
{
Stream s3Stream = requestS3.GetRequestStream();
s3Stream.Write(fileRawBytes, 0, fileRawBytes.Length);
Debug.Log("Sent bytes: " +
requestS3.ContentLength +
", for file: " +
fileName);
s3Stream.Close();
}
catch (Exception e)
{
return false;
}
return true;
}
}
@prnthp
Copy link
Author

prnthp commented Aug 3, 2021

Usage:

  1. Set up your IAM user and generate the token/secret.
  2. Attach this script to a GameObject.
  3. Call the UploadFile method with the full file path and filename.

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