Unity中如何将AR相机拍摄的照片保存至安卓图片图库?
如何将Unity截图保存到安卓图库
嘿,这个问题我刚好踩过坑,直接替换persistentDataPath成固定路径的话大概率会出问题——毕竟安卓不同版本的存储机制差异很大,尤其是安卓10之后的分区存储,而且就算你硬写路径成功了,系统图库也可能识别不到这张图。下面给你两种靠谱的解决方案:
方案1:调用安卓原生API插入媒体库(推荐,适配全版本)
这种方法会通过安卓的MediaStore将图片正式插入系统媒体库,自动处理权限和图库识别问题,还能自定义保存的子目录。
修改后的完整代码示例:
using UnityEngine; using System.IO; public class ARGallerySaver : MonoBehaviour { public void SaveARScreenshotToGallery(Texture2D screenshot) { // 生成带时间戳的文件名 string screenshotName = "Dorot" + System.DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss") + ".png"; // 先把图片写到临时缓存目录(避免权限问题) string tempFilePath = Path.Combine(Application.temporaryCachePath, screenshotName); File.WriteAllBytes(tempFilePath, screenshot.EncodeToPNG()); // 调用安卓原生代码处理媒体库插入 using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) { AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"); AndroidJavaObject contentResolver = currentActivity.Call<AndroidJavaObject>("getContentResolver"); using (AndroidJavaClass mediaStore = new AndroidJavaClass("android.provider.MediaStore")) { AndroidJavaObject contentValues = new AndroidJavaObject("android.content.ContentValues"); // 设置图片基本信息 contentValues.Call<AndroidJavaObject>("put", mediaStore.GetStatic<string>("Images.Media.DISPLAY_NAME"), screenshotName); contentValues.Call<AndroidJavaObject>("put", mediaStore.GetStatic<string>("Images.Media.MIME_TYPE"), "image/png"); // 根据安卓版本设置存储路径 int androidSdkVersion = new AndroidJavaClass("android.os.Build$VERSION").GetStatic<int>("SDK_INT"); if (androidSdkVersion >= 29) { // 安卓10+用RELATIVE_PATH指定图库子目录(比如Pictures/你的APP名) contentValues.Call<AndroidJavaObject>("put", mediaStore.GetStatic<string>("Images.Media.RELATIVE_PATH"), "Pictures/AR_Dorot"); } else { // 安卓9及以下直接指定文件路径 contentValues.Call<AndroidJavaObject>("put", mediaStore.GetStatic<string>("Images.Media.DATA"), tempFilePath); } // 插入到媒体库获取URI AndroidJavaObject imageUri = contentResolver.Call<AndroidJavaObject>("insert", mediaStore.GetStatic<AndroidJavaObject>("Images.Media.EXTERNAL_CONTENT_URI"), contentValues); if (imageUri != null) { // 将临时文件复制到媒体库指定位置 using (AndroidJavaObject outputStream = contentResolver.Call<AndroidJavaObject>("openOutputStream", imageUri)) using (AndroidJavaObject inputStream = new AndroidJavaObject("java.io.FileInputStream", tempFilePath)) { byte[] buffer = new byte[1024]; int readLength; while ((readLength = inputStream.Call<int>("read", buffer)) > 0) { outputStream.Call<void>("write", buffer, 0, readLength); } inputStream.Call<void>("close"); outputStream.Call<void>("close"); } // 删除临时文件 File.Delete(tempFilePath); // 通知系统媒体库更新,让图库立刻显示新图片 new AndroidJavaClass("android.media.MediaScannerConnection") .CallStatic("scanFile", currentActivity, new string[] { tempFilePath }, new string[] { "image/png" }, null); } } } // 恢复主Canvas显示 GameObject.Find("MainCanvas").GetComponent<Canvas>().enabled = true; } }
方案2:用Unity自带的CaptureScreenshot(简单但灵活性低)
如果你的需求只是快速保存全屏截图到图库,Unity自带的Application.CaptureScreenshot可以直接搞定,它会自动处理路径和基础权限:
public void QuickSaveScreenshotToGallery() { string screenshotName = "Dorot" + System.DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss") + ".png"; // 直接保存到系统图库,Unity会处理所有细节 Application.CaptureScreenshot(screenshotName); // 恢复Canvas显示 GameObject.Find("MainCanvas").GetComponent<Canvas>().enabled = true; }
不过这个方法有局限性:只能捕获整个屏幕,无法自定义存储子目录,也不能用你代码里已经截取好的Texture2D对象。
关键注意事项
- 权限配置:
- 安卓9及以下:在Unity Player Settings里勾选
WRITE_EXTERNAL_STORAGE权限 - 安卓10+:如果需要兼容旧存储逻辑,可以在
AndroidManifest.xml中添加android:requestLegacyExternalStorage="true"
- 安卓9及以下:在Unity Player Settings里勾选
- 测试环境:一定要用真实安卓设备测试,模拟器的图库经常会有延迟或者不识别的问题
- 媒体扫描:必须通知系统扫描新文件,否则图库可能要等很久才会显示新保存的图片
内容的提问来源于stack exchange,提问作者Montacer Dkhilali




