Unity StreamingAssets完全指南
一、基本概念
StreamingAssets是 Unity 中一个特殊的文件夹,位于Assets/StreamingAssets/。它的核心特点是:
📌打包时原样复制到最终应用中,不做任何压缩、加密或格式转换,可通过文件路径直接访问。
二、核心特性
| 特性 | 说明 |
|---|---|
| 原样保留 | 文件不会被 Unity 处理,保持原始格式 |
| 只读 | 运行时只能读,不能写 |
| 平台差异 | 不同平台访问方式不同(尤其是 Android) |
| 不参与打包压缩 | 会增加安装包体积 |
| 无引用检测 | Unity 不会追踪其中资源的引用关系 |
三、各平台路径
Unity 提供Application.streamingAssetsPath获取该目录路径:
| 平台 | 实际路径 |
|---|---|
| Editor / Standalone(PC/Mac) | 项目路径/Assets/StreamingAssets或<exe>_Data/StreamingAssets |
| iOS | <Application>/Data/Raw |
| Android | jar:file:///data/app/xxx.apk!/assets/⚠️ 在 APK 内部 |
| WebGL | URL/StreamingAssets |
四、读取方式
1. PC / iOS / Editor(可直接用 File API)
stringpath=Path.Combine(Application.streamingAssetsPath,"config.json");stringcontent=File.ReadAllText(path);2. Android(必须用 UnityWebRequest)⚠️
Android 上的 StreamingAssets 位于 APK 压缩包内,无法用 File API 直接读取,必须使用UnityWebRequest:
IEnumeratorLoadFromStreamingAssets(stringfileName){stringpath=Path.Combine(Application.streamingAssetsPath,fileName);#ifUNITY_ANDROID && !UNITY_EDITOR// Android 必须用 UnityWebRequest#elsepath="file://"+path;#endifUnityWebRequestrequest=UnityWebRequest.Get(path);yieldreturnrequest.SendWebRequest();if(request.result==UnityWebRequest.Result.Success){stringcontent=request.downloadHandler.text;Debug.Log(content);}}3. 统一封装(推荐)
publicstaticclassStreamingAssetsReader{publicstaticIEnumeratorReadText(stringrelativePath,Action<string>onComplete){stringpath=Path.Combine(Application.streamingAssetsPath,relativePath);#if!UNITY_ANDROID || UNITY_EDITORpath="file://"+path;#endifusing(UnityWebRequestreq=UnityWebRequest.Get(path)){yieldreturnreq.SendWebRequest();if(req.result==UnityWebRequest.Result.Success)onComplete?.Invoke(req.downloadHandler.text);elseDebug.LogError($"读取失败:{req.error}");}}publicstaticIEnumeratorReadBytes(stringrelativePath,Action<byte[]>onComplete){stringpath=Path.Combine(Application.streamingAssetsPath,relativePath);#if!UNITY_ANDROID || UNITY_EDITORpath="file://"+path;#endifusing(UnityWebRequestreq=UnityWebRequest.Get(path)){yieldreturnreq.SendWebRequest();if(req.result==UnityWebRequest.Result.Success)onComplete?.Invoke(req.downloadHandler.data);}}}五、典型应用场景
✅ 适合放的内容
- 游戏初始配置文件(JSON、XML、CSV)
- 首包 AssetBundle(热更新前的初始资源)
- 视频文件(播放前不需要处理)
- 数据库文件(如 SQLite 的
.db,需先复制到可写目录) - 第三方 SDK 配置文件
❌ 不适合放的内容
- 需要频繁修改的数据(该目录只读)
- Unity 需要识别的资源(如 prefab、材质等,应放 Assets 或 Resources)
- 敏感数据(明文可被解压查看)
六、StreamingAssets vs 其他目录对比
| 对比项 | StreamingAssets | Resources | persistentDataPath |
|---|---|---|---|
| 读写 | 只读 | 只读 | ✅ 可读写 |
| 是否打包 | ✅ 打入包体 | ✅ 打入包体 | ❌ 运行时生成 |
| 是否压缩 | ❌ 原样保留 | ✅ Unity 处理 | - |
| 访问方式 | 文件路径 | Resources.Load | 文件路径 |
| Android 特殊 | ✅ 需 WebRequest | 无 | 无特殊 |
| 典型用途 | 首包 AB、配置 | 少量常驻资源 | 存档、热更资源 |
七、常见使用模式:首次启动复制到可写目录
由于 StreamingAssets只读,常见做法是首次启动时复制到persistentDataPath,之后从可写目录读取和更新:
IEnumeratorCopyToPersistent(stringfileName){stringsrcPath=Path.Combine(Application.streamingAssetsPath,fileName);stringdstPath=Path.Combine(Application.persistentDataPath,fileName);if(File.Exists(dstPath))yieldbreak;// 已复制过#if!UNITY_ANDROID || UNITY_EDITORsrcPath="file://"+srcPath;#endifusing(UnityWebRequestreq=UnityWebRequest.Get(srcPath)){yieldreturnreq.SendWebRequest();if(req.result==UnityWebRequest.Result.Success){File.WriteAllBytes(dstPath,req.downloadHandler.data);Debug.Log($"复制完成:{dstPath}");}}}八、热更新架构中的典型作用
安装包首次启动: ┌─────────────────────┐ ┌──────────────────────┐ │ StreamingAssets │──复制→│ persistentDataPath │ │ (首包 AB + 版本号) │ │ (可写,支持更新) │ └─────────────────────┘ └──────────────────────┘ │ ↓ 远程 CDN 下载新版本 AB 覆盖 persistentDataPath加载顺序:优先从persistentDataPath读取(热更后的资源),若无则回退到StreamingAssets(首包资源)。
stringGetABPath(stringabName){stringpersistentPath=Path.Combine(Application.persistentDataPath,abName);if(File.Exists(persistentPath))returnpersistentPath;// 热更资源优先returnPath.Combine(Application.streamingAssetsPath,abName);// 首包资源兜底}九、注意事项 ⚠️
- Android 不能用 File.Exists 判断:APK 内文件用
File.Exists会返回 false,需用UnityWebRequest尝试请求。 - 路径不要用反斜杠:统一用
Path.Combine或/。 - 大文件谨慎放:直接影响安装包大小。
- Meta 文件不打包:
.meta文件不会随资源打入包体。 - 子目录支持:可以任意建子目录,路径需一致。
- iOS 大小写敏感:注意文件名大小写。
十、常见问题 FAQ
Q1: 为什么 Android 上 File.ReadAllText 读不到?
A: APK 是压缩包,文件不在真实文件系统中,必须用 UnityWebRequest。
Q2: StreamingAssets 里的文件会被压缩加密吗?
A: 不会,原样保留,容易被解包查看,不要放敏感内容。
Q3: 可以运行时写入吗?
A: 不能,请写入Application.persistentDataPath。
Q4: StreamingAssets 会打入 Resources 索引吗?
A: 不会,Unity 不会识别里面的资源。
