Unity编辑器扩展之上传文件至腾讯云对象存储桶

1. 需求

每次将资源打包成Bundle包或者需要将某些文件上传到对象存储桶中,都需要打开腾讯云或是其它第三方平台然后将文件上传到指定目录。上述操作自我感觉比较麻烦于是该工具就诞生了,在打包完资源后在Unity内部就能实现上传。当然你也可以在Unity内部上传任何你想上传的文件,也提供上传文件夹下的所有内容

2. 使用教程

1. 第一次使用需要进行一些基本的配置具体如下

2. 选择文件可以通过拖拽到指定区域或者点击选择文件、选择文件夹都可以实现,选择完成之后要上传的文件信息会在下方的上传文件列表中显示

2.1 如果是Project面板下的文件,点击名称会锁定该文件在Project面板中的具体位置,点击移除按钮则在文件列表删除该文件

3. 选择完文件后点击上传按钮即可,上传完成会弹出提示框

4. 当然你也可以对上传到存储桶中的数据进行操作,点击获取存储桶数据就可以看到你上传的内容,你可以选择单个移除亦或是选择清除该文件夹下的所有数据

3. 示例代码 

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEditor;
using Object = UnityEngine.Object;

public class FileUploader : EditorWindow
{
    private readonly List<FileModel> uploadFileList = new List<FileModel>();
    private readonly List<FileModel> cloudFileList = new List<FileModel>();
    private TencentCloudCosOperation tencentCloudCos;
    private bool isUploading = false;
    private Vector2 uploadScrollPos = Vector2.zero; // 滚动条位置
    private Vector2 cloudScrollPos = Vector2.zero; // 滚动条位置
    public bool isSettingData = false;

    [MenuItem("Tools/File Uploader")]
    public static void ShowWindow()
    {
        EditorWindow.GetWindow<FileUploader>("Upload Bundle");
    }


    private void OnEnable()
    {
        isUploading = false;
        isSettingData = EditorPrefs.GetBool("SettingData", false);
        if (isSettingData)
        {
            tencentCloudCos = new TencentCloudCosOperation(
                EditorPrefs.GetString("Region"),
                EditorPrefs.GetString("BucketName"),
                EditorPrefs.GetString("SecretId"),
                EditorPrefs.GetString("SecretKey"),
                EditorPrefs.GetString("SavePath"));
        }
    }

    private void OnGUI()
    {
        if (isSettingData)
        {
            DrawUploadArea();
            DrawFileList("上传文件列表", uploadFileList, ref uploadScrollPos, drawOther: DrawUploadListButtons);
            DrawFileList("云存储桶文件列表", cloudFileList, ref cloudScrollPos, (file) =>
            {
                Debug.Log("删除文件:" + file.path);
                tencentCloudCos.DeleteObject(file.path);
            }, DrawCloudBucketListButtons);
        }
        else
        {
            DrawSettingPanel();
        }
    }

    #region 配置数据

    private string region = "";
    private string bucketName = "";
    private string secretId = "";
    private string secretKey = "";
    private string savePath = "";

    /// <summary>
    /// 绘制配置面板
    /// </summary>
    private void DrawSettingPanel()
    {
        #region 配置数据

        GUIStyle loginStyle = new GUIStyle
        {
            padding = new RectOffset(10, 10, 10, 10),
        };
        GUIStyle labelStyle = new GUIStyle
        {
            fontStyle = FontStyle.Bold,
            fontSize = 18,
            alignment = TextAnchor.MiddleCenter,
            padding = new RectOffset(0, 0, 8, 8),
            normal = new GUIStyleState
                { textColor = Color.white }
        };
        GUILayout.Label("配置信息", labelStyle);
        GUILayout.BeginVertical(loginStyle);
        bucketName = EditorGUILayout.TextField("存储桶名称", bucketName);
        region = EditorGUILayout.TextField("所属地域", region);
        secretId = EditorGUILayout.TextField("SecretId", secretId);
        secretKey = EditorGUILayout.TextField("SecretKey", secretKey);
        savePath = EditorGUILayout.TextField("存储路径", savePath);

        GUILayout.Space(10);
        GUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace(); // 左侧空白区域
        if (GUILayout.Button("保存", GUILayout.Width(150)))
        {
            // 判断region是否为空
            if (region != "" && bucketName != "" && secretId != "" && secretKey != "")
            {
                EditorPrefs.SetString("Region", region);
                EditorPrefs.SetString("BucketName", bucketName);
                EditorPrefs.SetString("SecretId", secretId);
                EditorPrefs.SetString("SecretKey", secretKey);
                EditorPrefs.SetString("SavePath", savePath);
                EditorPrefs.SetBool("SettingData", true);
                tencentCloudCos = new TencentCloudCosOperation(region, bucketName, secretId, secretKey, savePath);
                isSettingData = true;
            }
            else
            {
                EditorUtility.DisplayDialog("提示", "请填写完整的配置信息", "确定");
            }
        }

        GUILayout.FlexibleSpace(); // 右侧空白区域
        GUILayout.EndHorizontal();

        DrawPromptInfo();

        GUILayout.EndVertical();

        #endregion
    }

    #endregion

    /// <summary>
    /// 绘制提示信息
    /// </summary>
    void DrawPromptInfo()
    {
        #region 提示信息

        GUIStyle tipStyle = new GUIStyle
        {
            fontStyle = FontStyle.Bold,
            fontSize = 14,
            alignment = TextAnchor.UpperLeft,
            padding = new RectOffset(0, 0, 5, 5),
            normal = new GUIStyleState
                { textColor = Color.green },
            fixedWidth = 200f,
        };
        GUILayout.BeginVertical();
        if (GUILayout.Button("所属地域:点击获取参考", tipStyle))
        {
            Application.OpenURL("https://cloud.tencent.com/document/product/436/6224");
        }

        if (GUILayout.Button("存储桶名称:点击获取参考", tipStyle))
        {
            Application.OpenURL("https://console.cloud.tencent.com/cos5/bucket");
        }

        if (GUILayout.Button("SecretId SecretKey:点击获取参考", tipStyle))
        {
            Application.OpenURL("https://console.cloud.tencent.com/cam/capi");
        }

        GUILayout.Button("存储路径:将对象上传到指定的文件夹或路径,可通过/实现,例如将 picture.png 上传到 doc/images 文件夹,设置为 doc/images", tipStyle);
        GUILayout.Button("最大支持上传5GB文件", tipStyle);
        GUILayout.EndVertical();

        #endregion
    }


    /// <summary>
    /// 绘制上传区域
    /// </summary>
    private void DrawUploadArea()
    {
        #region 上传区域

        DrawLabel("上传区域", DrawUploadAreaButtons);
        GUILayout.BeginHorizontal("box");
        Event evt = Event.current;
        Rect dropArea = GUILayoutUtility.GetRect(0.0f, 150.0f, GUILayout.ExpandWidth(true));

        DrawUploadAreaIcon(dropArea);

        GUI.Box(dropArea, "");
        if (evt.type == EventType.DragUpdated)
        {
            if (dropArea.Contains(evt.mousePosition))
            {
                DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
                evt.Use();
            }
        }
        else if (evt.type == EventType.DragPerform)
        {
            if (dropArea.Contains(evt.mousePosition))
            {
                DragAndDrop.AcceptDrag();
                foreach (string draggedPath in DragAndDrop.paths)
                {
                    if (Directory.Exists(draggedPath))
                    {
                        string[] filesInDirectory = Directory.GetFiles(draggedPath);
                        foreach (string filePath in filesInDirectory)
                        {
                            if (!filePath.EndsWith(".meta"))
                            {
                                if (File.Exists(filePath))
                                {
                                    var model = uploadFileList.Find(m => m.path == filePath);
                                    if (model == null)
                                    {
                                        uploadFileList.Add(new FileModel()
                                        {
                                            name = Path.GetFileName(filePath),
                                            path = filePath,
                                        });
                                    }
                                }
                            }
                        }
                    }
                    else if (File.Exists(draggedPath) && !draggedPath.EndsWith(".meta"))
                    {
                        var model = uploadFileList.Find(m => m.path == draggedPath);
                        if (model == null)
                        {
                            uploadFileList.Add(new FileModel()
                            {
                                name = Path.GetFileName(draggedPath),
                                path = draggedPath,
                            });
                        }
                    }
                }

                evt.Use();
            }
        }

        GUILayout.EndHorizontal();

        #endregion
    }

    /// <summary>
    /// 绘制上传区域Icon
    /// </summary>
    /// <param name="dropArea"></param>
    private void DrawUploadAreaIcon(Rect dropArea)
    {
        #region 绘制ICON
        Texture2D icon = AssetDatabase.LoadAssetAtPath<Texture2D>($"{GetIconPath($"{nameof(FileUploader)}.cs")}/Icons/UploadIcon.png");
        float iconSize = 64f;
        float iconX = dropArea.x + (dropArea.width - iconSize) / 2;
        float iconY = dropArea.y + (dropArea.height - iconSize) / 2;
        GUI.DrawTexture(new Rect(iconX, iconY - 20f, iconSize, iconSize), icon);

        // 添加文字
        string text = "将文件拖拽到此处";
        GUIStyle style = new GUIStyle(GUI.skin.label)
        {
            alignment = TextAnchor.MiddleCenter,
            fontSize = 15,
            fontStyle = FontStyle.Bold,
        };
        float textY = (iconY + iconSize) - 15f;
        GUI.Label(new Rect(dropArea.x, textY, dropArea.width, 20f), text, style);

        #endregion
    }

    private string GetIconPath(string scriptFileName)
    {
        string[] scriptPaths = Directory.GetFiles(Application.dataPath, scriptFileName, SearchOption.AllDirectories);
        if (scriptPaths.Length > 0)
        {
            string absolutePath = scriptPaths[0];
            string relativePath = "Assets" + absolutePath.Substring(Application.dataPath.Length);
            return Path.GetDirectoryName(relativePath);
        }
        else
        {
            return string.Empty;
        }
    }
    
    /// <summary>
    /// 绘制文件列表
    /// </summary>
    /// <param name="titleName"></param>
    /// <param name="fileList"></param>
    /// <param name="scrollPos"></param>
    /// <param name="removeCallback"></param>
    /// <param name="drawOther"></param>
    private void DrawFileList(string titleName, List<FileModel> fileList, ref Vector2 scrollPos,
        Action<FileModel> removeCallback = null, Action drawOther = null)
    {
        #region 文件列表

        GUIStyle fileListStyle = new GUIStyle(GUI.skin.label)
        {
            name = "box",
            padding = new RectOffset(10, 10, 10, 10)
        };
        DrawLabel(titleName, drawOther);
        GUILayout.BeginHorizontal(fileListStyle);
        // 创建可滚动区域
        scrollPos =
            GUILayout.BeginScrollView(scrollPos, GUILayout.Height(150), GUILayout.ExpandWidth(true));
        for (int i = 0; i < fileList.Count; i++)
        {
            GUILayout.BeginHorizontal("box");
            if (GUILayout.Button(
                    fileList[i].name.Substring(0, Mathf.Min(fileList[i].name.Length, 20)) +
                    (fileList[i].name.Length > 20 ? "..." : ""), EditorStyles.boldLabel)) // 文件名作为按钮
            {
                string path = fileList[i].path;
                Object obj = AssetDatabase.LoadAssetAtPath<Object>(path);
                EditorGUIUtility.PingObject(obj); // 选中文件
            }

            GUILayout.Label("路径: " + fileList[i].path, EditorStyles.boldLabel);
            if (GUILayout.Button("移除", GUILayout.Width(60))) // 设置按钮宽度为60
            {
                removeCallback?.Invoke(fileList[i]);
                RemoveFile(fileList, i);
            }

            GUILayout.EndHorizontal();
        }

        GUILayout.EndScrollView();
        GUILayout.EndHorizontal();

        #endregion
    }


    /// <summary>
    /// 绘制上传区域功能按钮
    /// </summary>
    void DrawUploadAreaButtons()
    {
        if (GUILayout.Button("上传", GUILayout.Width(100)) && !isUploading)
        {
            int index = 0;
            int fileCount = uploadFileList.Count;
            if (tencentCloudCos != null && fileCount > 0)
            {
                isUploading = true;
                EditorApplication.update = () =>
                {
                    if (index < fileCount)
                    {
                        tencentCloudCos.PutObject(uploadFileList[index].name, uploadFileList[index].path, (progress) =>
                        {
                            bool isCancel =
                                EditorUtility.DisplayCancelableProgressBar("上传进度", uploadFileList[index].name,
                                    progress / 100f);
                            if (isCancel || progress >= 100f)
                            {
                                EditorUtility.ClearProgressBar();
                                index++;
                                if (index >= fileCount)
                                {
                                    EditorUtility.DisplayDialog("提示", "所有文件上传完成", "确定");
                                    uploadFileList.Clear();
                                    EditorApplication.update = null; // 取消回调函数
                                    isUploading = false;
                                }
                            }
                        });
                    }
                };
            }
        }

        if (GUILayout.Button("选择文件夹", GUILayout.Width(100)))
        {
            string path = EditorUtility.OpenFolderPanel("选择文件", "", "");
            if (!string.IsNullOrEmpty(path))
            {
                string[] files = Directory.GetFiles(path);
                foreach (var file in files)
                {
                    uploadFileList.Add(new FileModel()
                    {
                        name = Path.GetFileName(file),
                        path = file
                    });
                }
            }
        }

        // 选择文件
        if (GUILayout.Button("选择文件", GUILayout.Width(100)))
        {
            string path = EditorUtility.OpenFilePanel("选择文件", "所有文件", "");
            if (!string.IsNullOrEmpty(path))
            {
                uploadFileList.Add(new FileModel()
                {
                    name = Path.GetFileName(path),
                    path = path
                });
            }
        }

        if (GUILayout.Button("清除配置数据", GUILayout.Width(100)))
        {
            EditorPrefs.DeleteKey("Region");
            EditorPrefs.DeleteKey("BucketName");
            EditorPrefs.DeleteKey("SecretId");
            EditorPrefs.DeleteKey("SecretKey");
            EditorPrefs.DeleteKey("SettingData");
            EditorPrefs.DeleteKey("SavePath");
            isSettingData = false;
        }
    }

    /// <summary>
    /// 绘制上传列表功能按钮
    /// </summary>
    private void DrawUploadListButtons()
    {
        if (GUILayout.Button("清空上传列表", GUILayout.Width(100)))
        {
            uploadFileList.Clear();
        }
    }

    /// <summary>
    /// 绘制云存储桶列表功能按钮
    /// </summary>
    void DrawCloudBucketListButtons()
    {
        if (GUILayout.Button("获取存储桶数据", GUILayout.Width(100)))
        {
            if (tencentCloudCos != null)
            {
                tencentCloudCos.GetBucketData(EditorPrefs.GetString("SavePath"), (contentsList) =>
                {
                    if (contentsList.Count > 1)
                    {
                        foreach (var item in contentsList.Where(item =>
                                     item.key.Substring(item.key.LastIndexOf("/", StringComparison.Ordinal) + 1) != ""))
                        {
                            if (cloudFileList.Find(file =>
                                    file.name ==
                                    item.key.Substring(item.key.LastIndexOf("/", StringComparison.Ordinal) + 1)) ==
                                null)
                            {
                                cloudFileList.Add(new FileModel()
                                {
                                    name = item.key.Substring(item.key.LastIndexOf("/", StringComparison.Ordinal) + 1),
                                    path = item.key
                                });
                            }
                        }
                    }
                    else
                    {
                        EditorUtility.DisplayDialog("提示", "没有可访问的数据", "确定");
                    }
                });
            }
        }


        if (GUILayout.Button("清空存储桶", GUILayout.Width(100)))
        {
            if (tencentCloudCos != null && cloudFileList.Count > 0)
            {
               bool isConfirm =  EditorUtility.DisplayDialog("提示", $"此操作会删除{EditorPrefs.GetString("SavePath")}存储桶下的所有文件,是否确认", "确定","取消");
               if (isConfirm)
               {
                   foreach (var file in cloudFileList)
                   {
                       tencentCloudCos.DeleteObject(file.path, () =>
                       {
                           EditorUtility.DisplayCancelableProgressBar("删除进度", file.name,
                               100 / 100f);
                       });
                   }
                   cloudFileList.Clear();
                   EditorUtility.ClearProgressBar();
                   EditorUtility.DisplayDialog("提示", "所有文件删除完成", "确定");
               }
            }
        }
    }


    private void RemoveFile(List<FileModel> fileList, int index)
    {
        fileList.RemoveAt(index);
    }

    [System.Serializable]
    public class FileModel
    {
        public string name;
        public string path;
    }

    /// <summary>
    /// 绘制标签
    /// </summary>
    /// <param name="titleName">标签名称</param>
    /// <param name="drawOther">标签后的内容</param>
    void DrawLabel(string titleName, Action drawOther = null)
    {
        GUIStyle horizontalStyle = new GUIStyle(GUI.skin.label)
        {
            padding = new RectOffset(10, 10, 10, 10),
            alignment = TextAnchor.MiddleCenter,
        };

        GUIStyle labelStyle = new GUIStyle(GUI.skin.label)
        {
            fontStyle = FontStyle.Bold,
            normal = new GUIStyleState() { textColor = Color.white }
        };

        GUILayout.BeginHorizontal(horizontalStyle);
        GUILayout.Label(titleName, labelStyle);
        drawOther?.Invoke();
        GUILayout.EndHorizontal();
    }
}

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今天喝水了嘛.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值