作者:禅与计算机程序设计艺术
1.简介
2019年,由英特尔、Facebook等公司联合举办的GDC大会上宣布了Unity Technologies将推出一个新品牌——Unity Game Development Platform(UGDP)。这个平台将包括对虚幻引擎4、Unreal Engine 4和原生Unity引擎的支持。在这个平台基础上,Unity Technologies推出了实时的多人在线网络游戏服务Photon Services,其具有以下优点:
- 支持Windows/Mac/Linux/IOS/Android
- 完全免费,无论项目规模大小
- 可扩展性强,可以支持百万级并发用户
- 支持WebGL/HTML5/PWA
- 可以快速部署到云端或本地服务器
基于这些优势,Unity Technologies开发人员开发了一套完整的Photon SDK,该SDK主要提供以下功能: - 游戏服务器和客户端之间的通信
- 用户角色的同步
- 物理引擎
- 声音引擎
- 好友系统
- 分组系统
- 多语言支持
- 聊天系统
- 数据存储
UGDP的目标是成为游戏领域的全面云端开发平台。GDC大会上发布的视频也指出,未来越来越多的游戏创作者将采用这个平台进行开发。Photon Unity Networking是基于Photon Services开发的一套完整的实时多人在线游戏开发解决方案。你可以用它快速地搭建起自己的在线虚拟现实游戏。
为了更加细化Photon Services,本文将按照如下顺序逐步阐述Photon Services的相关特性和功能。Photon Services的主要特色包含: - 服务模式:Photon Services提供了云端托管的服务模型,客户只需支付服务器运行成本,即可获得良好的服务体验;
- 开发门槛低:Photon Services提供了丰富的API,简单易懂,开发者可以轻松实现自己的游戏逻辑;
- 超高并发:Photon Services支持单机百万级并发,同时还提供了分布式集群架构,可以满足大型游戏团队的业务需求;
- 沉浸式游戏体验:Photon Services提供沉浸式的游戏体验,包括跨平台支持(Windows/Mac/Linux/IOS/Android)、WebGL/HTML5/PWA、即时互动聊天和多语言功能;
- 数据安全:Photon Services提供了数据存储和传输加密功能,保证数据的完整性、私密性和可用性;
- 免费试用:Photon Services提供了免费试用版本,开发者可以在不收取任何费用的情况下体验Photon Services的所有功能;
接下来,我将详细描述Photon Services的每一个功能模块。
2.基本概念术语说明
1.用户角色管理(Authentication and Authorization)
在Photon Services中,每个用户都被分配一个唯一的ID。当一个用户连接到服务器时,服务器通过身份验证过程确保用户的有效身份。身份验证完成后,服务器将给予用户一串令牌,作为标识符,用来区分不同的设备。这个过程通常称为“认证授权”。
通过身份验证和授权机制,服务器能够判断每个用户是否有权限访问某些资源。每个客户端都需要向服务器发送一个请求,声明自己所属的用户身份,并且要附带相应的令牌。如果服务器确认令牌正确,则允许用户访问资源。
服务器的身份验证和授权模块包含两个子模块:
- 用户注册:当用户第一次登录时,需要先创建一个账户,然后才能正常玩游戏。用户注册一般包含两方面的内容:用户名、密码、邮箱地址、手机号码。用户名、密码用于识别和认证用户身份,邮箱和手机号码可用于找回密码或者验证用户身份。
- 用户登录:用户可以通过用户名和密码登录到Photon Server,完成身份验证。服务器验证用户名和密码后,生成一个令牌,发给客户端,作为标识符,以便区别不同的设备。
用户登录后的身份验证信息会保存在服务器的一个临时数据库中,直到用户注销。
2.角色同步(Synchronization of User Roles)
当一个用户登录游戏客户端时,他需要在游戏服务器上找到其他玩家的位置、状态信息。这个过程称为“角色同步”。
Photon Services提供了一个角色同步框架,使得游戏客户端可以自动获取所有玩家的信息,包括位置、状态等。客户端只需要调用一个函数,就可以接收到所有玩家的最新状态。
每个玩家的最新状态都可以通过回调的方式返回给客户端。这样,客户端就可以根据玩家的位置、视野范围、角度等信息渲染出符合要求的虚拟世界。
3.物理引擎(Physics Engine)
角色同步的另一个重要功能就是实时响应服务器传来的物理事件。例如,服务器可能会广播一条消息,表示有某个玩家射击了一个物体,需要引擎计算和响应结果。
Photon Services的物理引擎是一个高性能的、完备的物理引擎库。它的设计目标是支持各种类型的游戏物理行为,包括刚体运动、碰撞检测、合力等。
物理引擎可以用于制作开放式世界,比如城市,也可以用于制作关卡内部,比如电子竞技比赛中的道具运动。
4.声音引擎(Sound Engine)
声音引擎负责处理各类游戏声音效果,包括背景音乐、音效、音轨切换、三维声效等。Photon Services的声音引擎使用OpenAL API实现,支持多种音频格式和编解码器。
声音引擎也可以用于在客户端播放本地的音效文件,提升游戏画面表现力。
5.好友系统(Friends System)
Photon Services提供了一个好友系统,可以让玩家之间建立联系,分享游戏里的数据和资源。服务器会记录每个玩家之间的关系,并把它们传送到客户端。客户端可以使用好友系统来搜索、加入和删除好友,邀请好友一起玩游戏。
对于游戏里的独特玩法,服务器也可以创建基于规则的“战队”,并将它们安排在一起进行比赛。每个队伍都有一个统一的管理者,由他来发起对局。
6.分组系统(Group System)
群组系统在游戏里可以用来组织和管理玩家。每个玩家都可以加入到多个群组中,并且每个群组里可以有多个成员。群组系统可以用于创建公共社交圈,让玩家之间更方便地进行协同工作。
群组系统还可以用于向游戏内广播通知,让整个社区都知道发生了什么事情。
7.多语言支持(Multi-Language Support)
Photon Services的多语言支持模块可以帮助游戏开发者提供不同语言版本的游戏。不同语言版本的游戏只需要在服务器端配置语言设置,客户端将自动切换显示对应的语言。
8.聊天系统(Chat System)
聊天系统是实时的沟通工具,可以让游戏里的玩家相互沟通。Photon Services的聊天系统使用户可以快速、直接地进行文字、语音、图文、表情等多种形式的沟通。
聊天系统还可以用来记录游戏里的历史数据,让玩家能够回顾过去的经历。
9.数据存储(Data Storage)
数据存储是个重要功能,它可以让游戏里的玩家保存一些自定义数据,例如存档、成绩、财产等。Photon Services提供了丰富的数据接口,包括文件上传下载、数据同步、数据查询等功能。
此外,游戏的开发者也可以从服务器获取一些统计数据,用于了解玩家的习惯和喜好。这些数据可以用来优化游戏的玩法。
3.核心算法原理及操作步骤
下面,我将详细阐述Photon Services的每一个功能模块,介绍其关键技术和核心算法原理,并给出详细的代码示例。
1.角色同步(Synchronization of User Roles)
角色同步是Photon Services最基础的功能。角色同步涉及到的算法有:
- 差异化同步(Differential Synchronization):服务器通过传输变化量而不是绝对值,减少网络流量,提升游戏性能;
- 漏斗式同步(Funnel Synchronization):服务器只传输角色的变化量,减少网络消耗;
- 平滑融合(Smoothing Fusion):服务器采用一种“时间平均”的方式,聚合各个客户端的角色数据,消除突变影响;
操作步骤:
- 客户端连接服务器;
- 服务器告诉客户端当前角色的初始状态;
- 客户端更新角色状态;
- 服务器计算角色的变化量;
- 服务器传输角色的变化量;
- 客户端接收角色的变化量;
- 客户端更新角色状态;
- 以此循环往复,直至两边角色同步完成。
using Photon.Realtime;
using Photon.Pun;
public class MyCharacter : MonoBehaviourPun, IPunObservable
{
private Vector3 _position = Vector3.zero;
void Start()
{
if (photonView.IsMine)
{
// We are the local player, so we can request the other players via RPC call
}
}
[PunRPC]
void UpdateOtherPlayerPosition(Vector3 position)
{
this._position = position;
transform.position = position;
}
void Update()
{
photonView.RPC("UpdateOtherPlayerPosition", RpcTarget.Others, transform.position);
// Do some calculations based on new role state
...
}
}
上面例子演示了如何使用角色同步。客户端脚本的Start()函数调用了photonView.IsMine条件,只有当客户端是主角色时才会执行,也就是说,只有本地玩家的角色信息才会被请求和同步。
更新角色状态的update()函数使用了photonView.RPC方法,通过远程过程调用(Remote Procedure Call),调用UpdateOtherPlayerPosition函数更新其他玩家的角色位置。这个函数通过远程调用,将自己的角色位置信息发送到其他客户端。
角色同步算法的实现部分使用了Vector3作为角色状态类型,但是实际上也可以使用其他类型,如Quaternion,Color等。另外,角色同步还可以利用PhotonView组件进行扩展,实现更复杂的场景中的角色同步。例如,角色可以拥有不同的移动方式、攻击能力、目标选择算法等,这些都可以用自定义属性来定义,然后服务器端将它们全部同步到客户端。
2.物理引擎(Physics Engine)
物理引擎是Photon Services的核心模块,也是很多网络游戏的基础模块。Photon Services的物理引擎是基于Bullet Physics API开发的,它可以模拟复杂的物理行为,如碰撞、弹簧、摩擦、阻尼等。
Bullet Physics API是专门为实时计算设计的高性能物理引擎,具有可靠的数学性能,适用于游戏、虚拟现实、机器人控制、交互式媒体等领域。
操作步骤:
- 创建刚体组件(Rigidbody Component);
- 将刚体添加到对象并赋予合理的质量、摩擦、阻力等属性;
- 为刚体添加一个碰撞体(Collision Shape Component);
- 使用相对位置(Relative Position)进行刚体位置和旋转同步;
- (可选)应用物理材料(Physical Materials);
- (可选)添加触发器(Triggers);
using UnityEngine;
using Photon.Pun;
public class MyCubeController : MonoBehaviourPun, IPunObservable
{
Rigidbody _rigidbody;
private float speed = 5f;
void Start()
{
_rigidbody = GetComponent<Rigidbody>();
if (!photonView.IsMine)
{
// We don't need to synchronize our movement, let's just move directly with interpolation
}
}
void Update()
{
if (photonView.IsMine && Input.GetKey(KeyCode.W))
{
_rigidbody.velocity += transform.forward * Time.deltaTime * speed;
}
else if (photonView.IsMine && Input.GetKey(KeyCode.S))
{
_rigidbody.velocity -= transform.forward * Time.deltaTime * speed;
}
if (photonView.IsMine && Input.GetKey(KeyCode.A))
{
_rigidbody.velocity -= transform.right * Time.deltaTime * speed;
}
else if (photonView.IsMine && Input.GetKey(KeyCode.D))
{
_rigidbody.velocity += transform.right * Time.deltaTime * speed;
}
if (_rigidbody!= null)
{
_rigidbody.velocity = Vector3.ClampMagnitude(_rigidbody.velocity, speed);
// Apply gravity force
_rigidbody.AddForce(new Vector3(0, -1, 0), ForceMode.Acceleration);
}
}
void FixedUpdate()
{
if (photonView.IsMine)
{
transform.position = _rigidbody.position;
transform.rotation = _rigidbody.rotation;
}
}
[PunRPC]
void SetVelocity(Vector3 velocity)
{
_rigidbody.velocity = velocity;
}
void OnTriggerEnter(Collider collider)
{
if (collider.GetComponent<PhotonView>() == null || collider.GetComponent<PhotonView>().IsMine)
{
return;
}
Debug.LogFormat("{0} entered trigger from {1}", gameObject.name, collider.gameObject.name);
}
}
上面例子演示了如何使用物理引擎。创建刚体组件后,将刚体赋予合理的质量、摩擦、阻力等属性。设置速度限制,防止角色跑得太快。处理角色的输入命令,并将它们转换为刚体的运动方向。将刚体的状态同步给服务器,并在FixedUpdate()函数里更新自己的transform。角色触发器(OnTriggerEnter())函数使用了远程过程调用(SetVelocity()),将刚体的速度信息发送到其他客户端。
物理引擎还可以用于处理复杂的物理场景,如交叉墙壁、楼梯、楼顶、房间隔离区域、触发器等,这些都需要高度的数学技巧才能实现精准模拟。
3.声音引擎(Sound Engine)
Photon Services声音引擎模块负责处理游戏声音,包括背景音乐、音效、音轨切换、三维声效等。Photon Services声音引擎使用OpenAL API实现,支持多种音频格式和编解码器。
操作步骤:
- 创建AudioSource组件;
- 设置音源类型、距离模型、音效混合等参数;
- 设置音效文件路径;
- 调整音效参数;
using Photon.Pun;
public class MusicManager : MonoBehaviourPun
{
AudioSource audioSource;
void Awake()
{
audioSource = GetComponent<AudioSource>();
}
[PunRPC]
void PlayMusic(string name)
{
string path = Application.streamingAssetsPath + "/music/" + name;
audioSource.PlayOneShot(Resources.Load<AudioClip>(path));
}
void OnJoinedRoom()
{
photonView.RPC("PlayMusic", RpcTarget.AllBuffered, "background_music");
}
}
上面例子演示了如何使用声音引擎。创建AudioSource组件,设置音源类型、距离模型、音效混合等参数。为AudioSource设置音效文件路径,并调用PlayOneShot()方法播放音效。初始化的时候,调用photonView.RPC()函数将背景音乐信息发送给所有客户端。
声音引擎还可以用于实现音频的动态范围(DOA)计算、动态定位、环境音效、音效捕获、音效编辑等功能。
4.好友系统(Friends System)
好友系统是Photon Services的高级功能之一。它提供了一个独立于游戏之外的社交功能,可以让玩家和其他玩家建立联系,分享游戏里的数据和资源。
操作步骤:
- 创建好友列表;
- 获取好友列表;
- 添加、删除、查找好友;
- 设置好友别名;
using Photon.Pun;
public class FriendManager : MonoBehaviourPun
{
static List<Friend> friendsList = new List<Friend>();
public class Friend
{
public string username;
public string alias;
public Friend(string uName, string aName)
{
username = uName;
alias = aName;
}
}
public bool AddFriend(string username, string alias)
{
foreach (Friend f in friendsList)
{
if (f.username == username)
{
return false; // Already exists
}
}
friendsList.Add(new Friend(username, alias));
return true;
}
public bool RemoveFriend(string username)
{
for (int i = 0; i < friendsList.Count; ++i)
{
if (friendsList[i].username == username)
{
friendsList.RemoveAt(i);
return true;
}
}
return false;
}
public bool FindFriend(string searchText, out List<Friend> foundList)
{
foundList = new List<Friend>();
foreach (Friend f in friendsList)
{
if (f.alias.ToLower().Contains(searchText.ToLower()))
{
foundList.Add(f);
}
}
return foundList.Count > 0;
}
}
上面例子展示了好友系统的实现。首先,创建一个FriendManager类的静态变量friendsList,用于保存好友列表。Friend类用于封装好友的用户名和别名。AddFriend()函数用来添加新的好友,RemoveFriend()函数用来删除好友,FindFriend()函数用来搜索好友。
当客户端加入房间之后,可以通过调用photonView.RPC()函数将好友列表发送给所有客户端。其他客户端可以读取好友列表,并做出相应的处理。例如,客户端可以显示好友列表,让玩家可以选择某个好友进行交谈。
5.分组系统(Group System)
分组系统是Photon Services另一个高级功能。它提供了一个集中管理多个玩家的能力,可以让玩家组织起来,并一起合作解决问题。
操作步骤:
- 创建组;
- 加入组;
- 离开组;
- 查询组成员;
using Photon.Pun;
public class GroupManager : MonoBehaviourPun
{
const byte MaxPlayersPerGroup = 4;
Dictionary<byte, List<int>> groups = new Dictionary<byte, List<int>>();
public bool CreateGroup(byte groupId)
{
if (groups.ContainsKey(groupId))
{
return false; // Already exists
}
groups.Add(groupId, new List<int>());
return true;
}
public bool JoinGroup(byte groupId)
{
if (photonView.IsMine && groups.ContainsKey(groupId) && groups[groupId].Count >= MaxPlayersPerGroup)
{
return false; // Full
}
int playerId = photonView.OwnerActorNr;
if (!groups.ContainsKey(groupId))
{
groups.Add(groupId, new List<int>());
}
groups[groupId].Add(playerId);
return true;
}
public bool LeaveGroup(byte groupId)
{
if (!groups.ContainsKey(groupId))
{
return false; // Not in group
}
groups[groupId].Remove(photonView.OwnerActorNr);
return true;
}
public bool QueryGroups()
{
SendGroupsToMaster();
return true;
}
[PunRPC]
void ReceiveGroups(Dictionary<byte, List<int>> receivedGroups)
{
groups = receivedGroups;
}
void SendGroupsToMaster()
{
photonView.Rpc("ReceiveGroups", RpcTarget.MasterClient, groups);
}
}
上面例子展示了分组系统的实现。首先,创建一个MaxPlayersPerGroup常量,表示每个组最多可以容纳多少玩家。接着,创建一个字典groups,用来存储每个组里的玩家列表。
创建组CreateGroup()函数通过GroupId检查是否已经存在,如果不存在就创建一个新的空的组。加入组JoinGroup()函数查找指定组是否存在,并且查看组成员数量是否达到最大值。若可以加入则将自己的ActorNumber加入到组里的玩家列表。同样的,离开组LeaveGroup()函数通过ActorNumber移除自己所在的组。查询组QueryGroups()函数发送组信息给主机客户端。
当客户端加入房间后,会调用SendGroupsToMaster()函数将组信息发送给主机客户端。主机客户端接收到组信息后,会调用ReceiveGroups()函数更新组成员。所有客户端都可以调用QueryGroups()函数获取组信息。
分组系统还可以用于共享游戏内容、团队排位、决策协商等功能。
6.多语言支持(Multi-Language Support)
Photon Services的多语言支持模块可以提供不同语言版本的游戏。游戏开发者只需要在服务器端配置语言设置,客户端会自动切换显示对应的语言。
操作步骤:
- 配置服务器语言设置;
- 客户端自动切换语言;
using Photon.Pun;
public class LanguageManager : MonoBehaviourPun
{
const byte DefaultLanguageCode = 0x01;
public enum LanguageCodes : byte
{
English = 0x01,
Spanish = 0x02,
French = 0x03,
German = 0x04,
Italian = 0x05
}
byte languageCode;
public bool ChangeLanguage(LanguageCodes langCode)
{
switch (langCode)
{
case LanguageCodes.English:
languageCode = (byte) LanguageCodes.English;
break;
case LanguageCodes.Spanish:
languageCode = (byte) LanguageCodes.Spanish;
break;
case LanguageCodes.French:
languageCode = (byte) LanguageCodes.French;
break;
case LanguageCodes.German:
languageCode = (byte) LanguageCodes.German;
break;
case LanguageCodes.Italian:
languageCode = (byte) LanguageCodes.Italian;
break;
}
SaveSettings();
return true;
}
void LoadSettings()
{
// Read settings from file or database here
languageCode = PlayerPrefs.GetInt("language", DefaultLanguageCode);
}
void SaveSettings()
{
// Write settings to file or database here
PlayerPrefs.SetInt("language", languageCode);
}
void Start()
{
LoadSettings();
photonView.RPC("ChangeLanguageRPC", RpcTarget.All, (LanguageCodes) languageCode);
}
}
上面例子展示了多语言支持的实现。首先,定义了默认语言编码DefaultLanguageCode,以及支持的语言编码LanguageCodes。配置服务器语言设置的方法是修改语言配置文件,并同步给所有客户端。客户端启动时,调用LoadSettings()函数读取当前语言设置,并通过photonView.RPC()函数通知所有客户端改变语言。
客户端语言设置的切换过程是在photonView.RPC()函数中执行的。服务器向所有客户端发送“改变语言”指令(ChangeLanguageRPC),各客户端接收到该指令后,调用LanguageManager对象的ChangeLanguage()函数更新语言设置。保存设置的方法可以在这里实现。
虽然Photon Services没有提供文本翻译工具,但开发者可以通过类似工具进行本地化开发。
7.聊天系统(Chat System)
聊天系统是一个实时的沟通工具,可以让玩家在游戏里进行随意的文字、语音、图文、表情等形式的交流。
操作步骤:
- 创建聊天室;
- 加入聊天室;
- 离开聊天室;
- 发送消息;
using Photon.Pun;
public class ChatManager : MonoBehaviourPun
{
const int MessageHistoryLength = 100;
public struct ChatMessage
{
public string sender;
public string message;
public ChatMessage(string sName, string msg)
{
sender = sName;
message = msg;
}
}
Dictionary<int, List<ChatMessage>> chatRooms = new Dictionary<int, List<ChatMessage>>();
public bool JoinChatRoom(int roomId)
{
if (chatRooms.ContainsKey(roomId))
{
return false; // Already joined
}
chatRooms.Add(roomId, new List<ChatMessage>());
return true;
}
public bool LeaveChatRoom(int roomId)
{
if (!chatRooms.ContainsKey(roomId))
{
return false; // Not in room
}
chatRooms.Remove(roomId);
return true;
}
public bool SendMessage(int roomId, string message)
{
if (!chatRooms.ContainsKey(roomId))
{
return false; // No such room
}
var history = chatRooms[roomId];
if (history.Count > MessageHistoryLength)
{
history.RemoveRange(0, history.Count - MessageHistoryLength);
}
var msg = new ChatMessage(photonView.Owner.NickName, message);
history.Add(msg);
SendMessageToOtherClients(roomInfo.Id, msg);
return true;
}
void SendMessageToOtherClients(int roomId, ChatMessage message)
{
foreach (KeyValuePair<int, GameObject> kv in PhotonNetwork.CurrentRoom.Players)
{
if (kv.Key!= photonView.owner.ActorNumber)
{
kv.Value.GetPhotonView(this).RPC("ReceiveMessage",
RpcTarget.OthersBufferedViaServer, roomId, message);
}
}
}
[PunRPC]
void ReceiveMessage(int roomId, ChatMessage message)
{
var messages = chatRooms[roomId];
if (messages.Count > MessageHistoryLength)
{
messages.RemoveRange(0, messages.Count - MessageHistoryLength);
}
messages.Add(message);
RenderMessagesInUI(roomId);
}
void RenderMessagesInUI(int roomId)
{
// Show messages in UI here
}
}
上面例子展示了聊天系统的实现。首先,创建一个ChatMessage结构,用于保存聊天消息的发送者和内容。定义了一个MessageHistoryLength常量,用于限制消息记录的最大长度。
创建聊天室JoinChatRoom()函数通过房间号roomId检查是否已经加入某个房间,如果没有则创建新的房间并加入到chatRooms字典中。离开聊天室LeaveChatRoom()函数通过roomId移除自己所在的房间。
发送消息SendMessage()函数通过roomId查找指定房间是否存在,并且向消息历史列表添加新的聊天消息。最后,调用SendMesssageToOtherClients()函数向其他客户端发送消息。客户端收到消息后,调用ReceiveMessage()函数更新消息历史列表,并调用RenderMessagesInUI()函数渲染到UI上。
聊天系统还可以用于私聊、频道讨论、游戏内聊天室等功能。
8.数据存储(Data Storage)
对于网络游戏来说,数据存储是非常重要的。它可以让玩家存储游戏数据,例如存档、成绩、设置、个人信息等。Photon Services提供的文件存储接口可以用来存储大型文件的容量,而且不需要额外付费。
操作步骤:
- 文件上传;
- 文件下载;
- 删除文件;
using Photon.Pun;
public class DataManager : MonoBehaviourPun
{
public async Task UploadFile(byte[] data, string fileName)
{
var webRequest = UnityWebRequest.Put($"http://myserver.com/{
fileName}", data);
await webRequest.SendWebRequest();
if (!webRequest.isHttpError &&!webRequest.isNetworkError)
{
Debug.Log("Upload successful!");
}
else
{
Debug.LogError("Upload failed! " + webRequest.error);
}
}
public async Task DownloadFile(string fileName)
{
var webRequest = UnityWebRequest $"http://myserver.com/{
fileName}";
await webRequest.SendWebRequest();
if (!webRequest.isHttpError &&!webRequest.isNetworkError)
{
var downloadedData = webRequest.downloadHandler.data;
ProcessDownloadedData(downloadedData);
}
else
{
Debug.LogError("Download failed! " + webRequest.error);
}
}
public bool DeleteFile(string fileName)
{
var filePath = Path.Combine(Application.persistentDataPath, fileName);
File.Delete(filePath);
return true;
}
}
上面例子展示了数据存储的实现。文件上传UploadFile()函数通过UnityWebRequest对象异步发送HTTP PUT请求,并等待响应。文件下载DownloadFile()函数通过UnityWebRequest对象异步发送HTTP GET请求,并等待响应,并读取响应数据ProcessDownloadedData()函数用来处理下载的数据。删除文件DeleteFile()函数通过本地文件系统删除指定的文件。
文件存储接口还有很多可以扩展的地方,例如文件搜索、文件分享、版本控制、权限管理等。