You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity项目中如何实现游戏生成文件至Web/Android/iOS设备的下载功能

Unity跨平台文件下载实现方案

以下是针对WebGL、Android、iOS三个平台的具体实现方案,无需依赖AssetStore插件,直接通过原生API和少量原生代码即可完成:

WebGL平台

WebGL受浏览器沙箱限制,无法直接写入本地文件,需借助浏览器的下载API触发用户下载。核心思路是将文件数据转为可被浏览器识别的格式,通过JS触发下载链接。

C#代码

using UnityEngine;
using System.IO;

public class FileDownloader : MonoBehaviour
{
    public void TriggerWebGLDownload(string fileName, byte[] fileData)
    {
        // 将字节数组转为Base64(小文件适用)
        string base64Data = System.Convert.ToBase64String(fileData);
        // 调用嵌入的JS函数
        Application.ExternalEval($"initFileDownload('{fileName}', '{base64Data}')");
    }
}

WebGL模板JS代码

在WebGL打包后的index.html中添加以下JS函数(或直接修改Unity的WebGL模板文件):

function initFileDownload(fileName, base64Data) {
    const link = document.createElement('a');
    link.href = `data:application/octet-stream;base64,${base64Data}`;
    link.download = fileName;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
}

注意:大文件建议改用Blob对象优化性能,将C#中的字节数组直接传递给JS,替换上述JS函数为:

function initFileDownload(fileName, arrayBuffer) {
    const blob = new Blob([arrayBuffer]);
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = fileName;
    link.click();
    URL.revokeObjectURL(url);
}

对应的C#代码需调整为通过SendMessage传递字节数组,避免Base64编码开销。

Android平台

Android可直接写入外部存储,需先申请读写权限,再将文件写入公共目录,最后通知系统刷新媒体库。

权限配置

在Plugins/Android/AndroidManifest.xml中添加权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

C#代码

using UnityEngine;
using System.IO;
using UnityEngine.Android;

public class FileDownloader : MonoBehaviour
{
    public void TriggerAndroidDownload(string fileName, byte[] fileData)
    {
        // 检查权限
        if (!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite))
        {
            Permission.RequestUserPermission(Permission.ExternalStorageWrite);
            return;
        }

        // 写入外部存储根目录
        string savePath = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, fileName);
        File.WriteAllBytes(savePath, fileData);

        // 通知系统刷新媒体库
        using (var mediaScanner = new AndroidJavaClass("android.media.MediaScannerConnection"))
        using (var context = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic<AndroidJavaObject>("currentActivity"))
        {
            mediaScanner.CallStatic("scanFile", context, new string[] { savePath }, null, null);
        }
    }
}

注意:Android 10及以上推荐使用Scoped Storage,避免直接操作外部存储根目录,可改用MediaStore API写入公共媒体目录。

iOS平台

iOS需将文件写入应用的Documents目录,再通过UIDocumentInteractionController唤起系统分享面板,让用户选择保存到本地或其他应用。

C#代码

using UnityEngine;
using System.IO;
using System.Runtime.InteropServices;

public class FileDownloader : MonoBehaviour
{
    [DllImport("__Internal")]
    private static extern void ShowFileSharingPanel(string filePath);

    public void TriggeriOSDownload(string fileName, byte[] fileData)
    {
        // 写入应用Documents目录
        string docsPath = Application.persistentDataPath;
        string savePath = Path.Combine(docsPath, fileName);
        File.WriteAllBytes(savePath, fileData);

        // 唤起系统分享面板
        if (Application.platform == RuntimePlatform.IPhonePlayer)
        {
            ShowFileSharingPanel(savePath);
        }
    }
}

iOS原生代码

在Plugins/iOS目录下创建FileSharer.m文件,添加以下代码:

#import <UIKit/UIKit.h>

@interface FileSharer : NSObject <UIDocumentInteractionControllerDelegate>
@end

@implementation FileSharer

UIDocumentInteractionController* _docController;

+ (void)showSharingPanelForFile:(NSString*)filePath {
    NSURL* fileURL = [NSURL fileURLWithPath:filePath];
    _docController = [UIDocumentInteractionController interactionControllerWithURL:fileURL];
    _docController.delegate = self;

    // 获取当前顶层ViewController
    UIViewController* currentVC = [UIApplication sharedApplication].keyWindow.rootViewController;
    while (currentVC.presentedViewController) {
        currentVC = currentVC.presentedViewController;
    }

    [_docController presentOptionsMenuFromRect:CGRectZero inView:currentVC.view animated:YES];
}

- (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller {
    return [UIApplication sharedApplication].keyWindow.rootViewController;
}

@end

extern "C" {
    void ShowFileSharingPanel(const char* filePath) {
        NSString* nsFilePath = [NSString stringWithUTF8String:filePath];
        [FileSharer showSharingPanelForFile:nsFilePath];
    }
}

内容的提问来源于stack exchange,提问作者Happy_cat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.01 21:23:11