Unity接入AWS S3过程,AWS SDK for Unity 踩坑记录

AWS S3,亚马逊的资源服务器服务,最近在研究这一块内容。记录一下踩坑的内容。

研究了AWS SDK for unity和AWS SDK for .net 两部分
unity很难接,主要原因是官方没有api,完全是参考.net版本的使用来用unity版本的。


AWS SDK的API命名不管啥平台都差不多,主要分3个系列

  1. List 查询
  2. Get 获取,下载
  3. Put 上传

主要的接口:

初始化

AmazonS3Client

m_AWSClient = new AmazonS3Client(strAccessKey, strSecretKey, regionEndpoint)

构造时候需要传入AccessKey,SecretKey,EndPoint

对象操作

GetObject
PutObject

Unity下出现了问题
GetObject是没办法获取下载进度的,所以弃用了这个Api,改用了UnityWebRequest进行下载,然后就出了一堆异常了,这边记两个关键点:

AWSConfigs.HttpClient = AWSConfigs.HttpClientOption.UnityWebRequest;
public CertificateHandlerPublicKey Certificate { get; private set; } = new CertificateHandlerPublicKey();

public class CertificateHandlerPublicKey : CertificateHandler
{
    protected override bool ValidateCertificate(byte[] certificateData)
    {
        return true;
    }
}

uwq = UnityWebRequest.Get(strURL);
uwq.certificateHandler = AWSProxy.Instance.Certificate;

设置完之后就正常了。

PutObject,上传一个文件。通常上传这个操作我们会做在Editor下非Runtime的,但是AWS就出现了一个很奇怪的事情,主对象AmazonS3Client出现了一个报错,说没有初始化。AmazonS3Client在new之前,需要调用

UnityInitializer.AttachToGameObject(this.gameObject);

瞬间就惊呆了,这是个Runtime的API,也就是说上传需要在Unity运行时。测试后发现,的确是可以的。

权限操作

GetACL
PutACL
Get没啥问题,Put在unity下尝试了,结果报了一个WebRequestHeader的错误,emm…按照经验来说请求缺少了头信息,那补全header信息就好了,但是AWS的SDK把内容全封装dll了。这就尴尬了。总不见得截包,再传出去。
于是与又尝试了.net版本,完全没问题,可以正常用。这就很神奇了。不过能用就行。

获取列表

ListObjects
在获取列表时候,遇到了个问题,aws sdk请求某个路径下内容,最多返回1000个文件信息,再多就没有了。调试发现返回的结构ListObjectsResponse 里,有个字段 NextMarker
在ListObjectsRequest里面也有个字段Marker。尝试了一下把第一次返回的mark传给下一次请求,可以获取成功。所以AWS是按照分页的逻辑返回的列表信息,上限1000。仔细一想也很合理,出于性能和传输带宽考虑,为了避免文件量太大,采用分页的方式。

		public void GetList(string strUrlPre, out bool isSuc, out Dictionary<string, long> result)
		{
			isSuc = true;
			result = new Dictionary<string, long>();

			Debug.Log("AWSApi Connect,Start GetList");

			string strMark = null;
			while (true)
			{
				ListObjectsRequest request = new ListObjectsRequest();
				request.BucketName = m_strBucket;
				request.Prefix = strUrlPre;
				request.Marker = strMark;
				try
				{
					ListObjectsResponse response = m_AWSClient.ListObjects(request);
					var listServer = response.S3Objects;
					for (int i = 0; i < listServer.Count; i++)
					{
						result.Add(listServer[i].Key, listServer[i].Size);
					}
					strMark = response.NextMarker;
					if (string.IsNullOrEmpty(strMark))
					{
						break;
					}
				}
				catch (System.Exception e)
				{
					isSuc = false;
					Debug.LogError("AWSApi Connect, Error : " + e.Message);
					break;
				}
			}
		}

下载AWS S3上一个文本时候遇到了一个问题
通过UnityWebRequest下载文本和取得text信息,发现string内容和上传的内容不同。

var task = UnityWebRequest.Get(strNetPath);
string strResult = task.downloadHandler.text;

下载完成后得到 task.downloadHandler.text;
调试看内容,和我上传时候用的文本内容一样。但是之后的逻辑里对文本内容解析失败了。
调试看string内容也是一样的,于是拿string.toChatArray()去查每一个字符有啥区别,发现了字符串看上去一样,但是首位字符是个空char,string.trim对这个空char无效,因为这个空char和第二位char组成了第一个string字。

byte[] bytes = task.downloadHandler.data;

获取bytes也是不对的,多了开头的空内容。

百度这个问题得到了一个解释:https://blog.csdn.net/shersfy/article/details/54614720
算是合理吧…加入代码。

string strResult = task.downloadHandler.text;
if (strResult[0] == 65279)
{
    strResult = strResult.Substring(1);
}
callBack?.Invoke(true, strResult);

然后就是用.net制作工具问题了

官方文档里面对.net的描述是在visual studio里的扩展可以搜索aws,可以找到插件。
我装了发现没啥用,一脸懵,完全没有说明。

于是手动下载了.net的dll
截止2021年9月3日有效,https://sdk-for-net.amazonwebservices.com/latest/v3/aws-sdk-net45.zip
如果无效了,那就上官方网站的搜索框里面搜sdk for .net找找了,找起来实在是麻烦。
解压后里面的dll,加入引用,windows下撸了一发工具出来。

因为一般发包都是双平台,ios和android。android在windows下发就可以了。ios打包必定在mac机器上。
所以尝试了一下,在mac上面跑.net。
最直接的方法是虚拟机。

我的机型是M1芯片,oh my god,截止2021 9 1日只有parallels desktop可以在M1芯片上支持windows虚拟机,装完系统,装好.net。运行… 很好没有反应

于是替换了方案,因为mac下可以直接运行.net了,这个可以自行百度。
donet可以直接拉起 .net core web的控制台应用。
尝试了hellow world,可以正常运行没有问题。

继续尝试AWS SDK for .net 的相关接口,失败了。报错内容是AWSConfig在进行一个本地的file操作,找不到路径。
over,懒得搞,宣布失败

所以还是用SVN在内网同步需要上传的内容,在windows机器上操作把。


然后是打包,打包APK没有问题,但是运行起来会发现
AmazonS3Client在new的时候,会抛出异常MissingMethodException。
AWS SDK for Unity,导入后里面有好多dll,so文件里和log信息没啥关系。
所以问题出来dll上,查看dll会发现
在这里插入图片描述
最后Targets .net 3.5
unity2019默认是 .net2.0,那就改呗,把Unity的.net版本提高。
然后发现,还是会出现错误MissingMethodException.
想一想,运行时,代码找不到,但是在PC上是好的,那有可能是被代码裁减了,那么就加上link.(link.xml unity代码裁减相关,可以百度到)

<?xml version="1.0" encoding="utf-8"?>
<linker>
  <assembly fullname="AWSSDK.CognitoIdentity" preserve="all" />
  <assembly fullname="AWSSDK.Core" preserve="all" />
  <assembly fullname="AWSSDK.S3" preserve="all" />
  <assembly fullname="AWSSDK.SecurityToken" preserve="all" />
</linker>

继续,依旧出现报错,出现在

AWSConfigs.HttpClient = AWSConfigs.HttpClientOption.UnityWebRequest;

这一行代码里面出现了,依旧是裁减问题

NullReferenceException: Object reference not set to an instance of an object.
      at Amazon.Runtime.Internal.UnityWebRequestWrapper..cctor () [0x00000] in <00000000000000000000000000000000>:0 
      at Amazon.AWSConfigs.set_HttpClient (Amazon.AWSConfigs+HttpClientOption value) 

AWS的内部异常是:
TypeInitializationException

经过一顿尝试

<linker>
  <assembly fullname="UnityEngine">
    <type fullname="UnityEngine.Networking.UnityWebRequest" preserve="all" />
    <type fullname="UnityEngine.Networking.UploadHandlerRaw" preserve="all" />
    <type fullname="UnityEngine.Networking.UploadHandler" preserve="all" />
    <type fullname="UnityEngine.Networking.DownloadHandler" preserve="all" />
    <type fullname="UnityEngine.Networking.DownloadHandlerBuffer" preserve="all" />
  </assembly>
	<assembly fullname="AWSSDK.CognitoIdentity" preserve="all"/>
	<assembly fullname="AWSSDK.Core" />
	<assembly fullname="AWSSDK.S3" />
	<assembly fullname="AWSSDK.SecurityToken" />
</linker>

呜呼,跑通了。

然后在跑逻辑过程中,发现了问题
AWS SDK的API中的List相关,例如ListBucketsAsync,在Android下跑的时候,不会有任何CallBack,也没有超时,也没有遗产,很纳闷。因为AWS SDK里没有证明连接成功的API,所以想用桶子信息尝试是否可以连接成功,这个想法是失败了。

再次吐槽
查看AWS官方的API实在没啥说明,官方也没有unity的文档,只能进论坛找,但是这种错误很难搜索到。

所以还是用Unity的API吧。UnityWebRequest。

安全性方面:

AWSProxy.Instance.GenerateDynamicURL

这个API尝试是可以用的,可以正确返回URL。
主要是产出一串带有加密信息的URL。
初始化时候的账号有权限,就可以成功下载,不然会返回HTTP错误。


程序学无止尽。
欢迎大家沟通,有啥不明确的,或者不对的,也可以和我私聊
我的QQ 334524067 神一般的狄狄

猜你喜欢

转载自blog.csdn.net/qq_37776196/article/details/120034272
今日推荐