基于百度API的.NET人脸识别项目实现
百度人脸识别API是一组高度可定制的云服务,它利用深度学习技术提供人脸检测和识别功能。开发者可以通过简单的API调用,实现人脸检测、特征提取、人脸比对、以及人脸识别等功能。在实际应用中,根据不同的业务场景,开发者可能需要设置一些自定义参数。例如,设置识别的类型(人脸检测、人脸识别等),或对返回结果的格式进行定制。
简介:本项目将介绍如何在.NET开发环境中实现人脸识别,利用百度AI平台提供的API完成人脸检测、比对、搜索等功能。内容包括创建应用获取API凭证,调用百度提供的.NET SDK,以及手动实现HTTP请求,处理服务器响应,并确保调用的安全性和性能优化。项目细节将取决于开发者具体实现。
1. 百度AI平台应用创建及API Key获取
初识百度AI平台
百度AI开放平台是百度公司提供的面向开发者的一站式人工智能产品和服务。该平台涵盖了语音、图像、自然语言处理等多个领域的功能,通过开放API的方式,使得开发者可以快速实现相关AI功能的应用开发。
创建应用并获取API Key
在使用百度AI开放平台的API服务之前,开发者需要在平台注册账号,并创建自己的应用来获取相应的API Key和Secret Key。以下是创建应用并获取API Key的基本步骤:
- 登录百度AI开放平台(ai.baidu.com);
- 在控制台选择创建应用,填写应用名称和所需服务类别;
- 完成应用创建后,系统会自动分配给应用唯一的API Key和Secret Key。
注意,API Key和Secret Key是使用百度AI开放平台服务的安全凭证,应妥善保管,不要泄露。
API Key的应用示例
获取到API Key和Secret Key后,就可以在自己的应用中集成百度AI平台提供的各种服务。以人脸检测API为例,我们将在后续章节中详细讨论如何应用这些API以及如何进行服务器响应数据的解析与应用。
通过本章节的学习,您已经完成了在百度AI平台的应用创建和API Key的获取工作,为接下来学习如何使用百度AI的各种API服务打下了基础。
2. 百度人脸识别API的使用方法
2.1 人脸识别API的基本概念
2.1.1 API的功能简介
百度人脸识别API是一组高度可定制的云服务,它利用深度学习技术提供人脸检测和识别功能。开发者可以通过简单的API调用,实现人脸检测、特征提取、人脸比对、以及人脸识别等功能。
2.1.2 API的使用场景
人脸识别API的应用场景非常广泛,从安全验证、人员考勤、到个性化推荐系统都有涉及。举个例子,在金融服务领域,可以通过人脸识别提高账户登录的安全性。在零售行业,可用来分析消费者的年龄和性别,从而实现更加个性化的营销策略。
2.1.3 API的访问和权限管理
使用API前,首先需要在百度AI开放平台申请权限并获取API Key和Secret Key。API Key是公开的,用于标识身份;而Secret Key则需要保密,用于加密签名,保护API请求不被篡改。
2.2 人脸识别API的参数和返回值
2.2.1 必要参数说明
人脸识别API需要以下必要参数:
image: 图片的Base64编码字符串或图片的URL地址。image_type: 图片类型,BASE64或URL。
2.2.2 可选参数及其影响
可选参数包括:
face_field: 返回信息中所包含的人脸信息字段。默认为age,beauty,faceshape,expression,faceset,landmark3d,landmark3d_version,landmark3d_index,landmark2d,landmark2d_index,quality,person,tag,emotion,facial_shape,smiling。group_id_list: 用于指定按照组ID过滤人脸库中的数据。
2.2.3 返回值解析
返回值通常是一个JSON格式的字符串,包含了检测结果、人脸ID、以及可能的错误信息。例如:
{
"error_code": 0,
"error_msg": "OK",
"face_list": [
{
"face_id": "a1b2c3d4-5678-efab-cdef-9876543210ab",
"face_token": "a1b2c3d4-5678-efab-cdef-9876543210ab",
"location": {
"left": 321,
"top": 123,
"width": 640,
"height": 480
},
"quality": {
"score": 0.99,
"blurness": 0.000001,
"occlusion": {"left Eye": 0.00, "right Eye": 0.00, "nose": 0.00, "mouth": 0.00},
"expression": {"anger": 0.00, "contempt": 0.00, "disgust": 0.00, "fear": 0.00, "happiness": 0.99, "neutral": 0.01, "sadness": 0.00, "surprise": 0.00}
},
"landmark": {
"eye_left": {"x": 214.43, "y": 133.58},
"eye_right": {"x": 464.93, "y": 135.57},
"nose": {"x": 340.75, "y": 178.38},
"mouth_left": {"x": 278.76, "y": 195.48},
"mouth_right": {"x": 416.92, "y": 193.90},
"face_type": 0,
"angle": {"pitch": 1.62, "roll": 5.31, "yaw": -11.30},
"blur": {"value": 1, "level": "high"}
}
}
]
}
其中, face_id 和 face_token 可用于后续的人脸识别请求, location 提供了人脸在图片中的位置, quality 提供了质量评估, landmark 提供了人脸关键点坐标。
每个API调用都可能因为输入参数的不正确或系统错误返回错误信息。错误信息通常包含一个错误代码 error_code 和对应的错误描述 error_msg 。
2.2.4 安全性注意事项
在实际应用过程中,开发者需要确保API请求的安全性。这意味着,应当对所有通过API传递的信息进行加密处理,并且确保所有敏感数据的传输都使用HTTPS协议。同时,API密钥不应该在客户端暴露,以避免被恶意使用。
接下来的章节将更详细地讲解如何通过.NET平台集成和使用百度的人脸识别API,并分析如何处理和解析返回的数据。
3. .NET SDK的安装及集成
.NET开发者们可以借助百度AI平台提供的SDK来加速开发过程,让调用AI服务变得如同使用本地库一样简单。这一章节将引导你完成.NET SDK的安装、配置以及如何将其集成到你的项目中。
3.1 .NET SDK的环境配置
3.1.1 安装步骤和环境要求
为了在.NET项目中使用百度AI平台提供的SDK,首先需要安装支持.NET的软件开发环境。推荐使用Visual Studio 2017或更高版本,并确保在安装时选择了“.NET桌面开发”和“.NET Core跨平台开发”工作负载。接下来,按照以下步骤安装SDK:
- 在NuGet包管理器中搜索
Baidu.Aip。 - 选择正确的包版本,并点击“安装”按钮,等待安装完成。
为确保兼容性,建议开发者查看SDK的官方文档以了解具体的版本要求和依赖。
3.1.2 SDK兼容性问题排查
在安装完成后,开发者可能会遇到兼容性问题。为了解决这些潜在问题,下面是一些排查技巧:
- 确认.NET Framework版本和SDK版本之间的兼容性。
- 使用
dotnet命令行工具来检查SDK是否安装成功,例如dotnet add package Baidu.Aip。 - 查阅官方文档或社区论坛,查看是否有人报告了类似问题,并获取解决方案。
- 如果出现缺少依赖项的错误,可以使用NuGet包管理器的“修复”功能来解决。
3.2 .NET与百度AI SDK的整合
3.2.1 SDK的引入和初始化
整合SDK到项目中,通常包括添加引用和配置访问密钥。以下是具体步骤:
- 在项目中添加对
Baidu.Aip的引用。 - 在代码中引入命名空间:
using Baidu.Aip.Face;。 - 初始化SDK客户端,需要传入
APP_ID、API_KEY和SECRET_KEY。
var client = new FaceClient(APP_ID, API_KEY, SECRET_KEY);
初始化过程中,SDK会根据提供的认证信息进行配置,确保之后的API调用能够成功认证。
3.2.2 封装SDK的基本方法和接口
为了更好地管理SDK的使用和减少重复代码,开发者可以创建一个封装类,这样在不同的项目中复用就变得更加简单。下面是一个简单的封装类示例:
public class BaiduFaceApi
{
private readonly FaceClient _faceClient;
public BaiduFaceApi(string appId, string apiKey, string secretKey)
{
_faceClient = new FaceClient(appId, apiKey, secretKey);
}
public string Detect(string imageUri)
{
var option = new FaceDetectOption()
{
Image = imageUri
};
var result = _faceClient.Common.Detect(option);
return result.ToString();
}
}
在上面的代码中, Detect 方法封装了对百度人脸识别API的调用。这种方式使得代码更加清晰,并且易于管理。
通过这一章节的学习,.NET开发者已经掌握了如何安装和配置百度AI的SDK,并将其集成到项目中。接下来的章节会介绍如何手动实现HTTP请求调用接口,并准备发送请求参数。
4. 手动实现HTTP请求调用接口
4.1 HTTP协议基础
4.1.1 请求/响应模型详解
超文本传输协议(HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议。在人脸检测API的调用中,HTTP协议提供了一种简单、统一的方式,让客户端与服务器之间进行数据传输。
HTTP协议遵循请求/响应模型,其中客户端发送一个HTTP请求到服务器,然后服务器返回一个HTTP响应。请求和响应都由状态行、头部(Headers)、空行(只存在头部之后)和可选的消息体组成。
请求由三个部分组成: 1. 请求行:包含了请求方法、请求的URI和HTTP版本。 2. 请求头:提供有关请求的其他信息,如用户代理、接受的内容类型、内容长度等。 3. 请求数据(可选):如果请求中包含了数据(如POST请求),则此部分包含请求的实际内容。
响应同样由三个部分组成: 1. 状态行:包含了HTTP版本、状态码及状态码的原因短语。 2. 响应头:提供了响应的元信息,比如服务器类型、日期、内容类型等。 3. 响应体(可选):包含了响应的数据,比如请求的页面内容。
理解这些基本概念对于进行有效的API调用至关重要,因为不同的请求方法(GET、POST、PUT、DELETE等)和头部配置可能会对人脸检测API的调用结果产生影响。
4.1.2 HTTP方法(GET/POST/PUT/DELETE)使用
HTTP方法定义了客户端和服务器端之间的操作类型。最常用的四种方法是GET、POST、PUT和DELETE。
- GET:请求从服务器获取特定的资源。当客户端发送一个GET请求到服务器时,服务器返回被请求的数据。
- POST:向指定资源提交数据进行处理请求,常用于向服务器提交表单或上传文件。数据被包含在请求体中发送给服务器。
- PUT:从客户端向服务器传送的数据取代指定的文档的内容。
- DELETE:请求服务器删除指定的资源。
在人脸检测API的调用中,通常会用到GET和POST方法。例如,通过GET方法获取API的使用文档,或者使用POST方法发送人脸图像数据给API进行检测。
4.2 人脸检测接口的手动调用
4.2.1 构造请求URL和参数
在手动调用API时,首先需要构建一个符合要求的URL和查询参数。这个URL通常包含了API的端点、必要的路径以及必要的查询参数。
在构造请求URL时,以下为一个基本的HTTP请求的示例格式:
http://api.example.com/path?param1=value1¶m2=value2
对于百度人脸识别API,可能看起来像这样:
https://aip.baidubce.com/rest/2.0/face/v3/detect?access_token=您的Access_Token
在上述URL中, https://aip.baidubce.com/rest/2.0/face/v3/detect 是API的端点,而 access_token 是必须传递的一个查询参数,它用于验证请求的合法性。
此外,还可以在请求中添加其他可选参数,如图像的URL、图像数据、图像质量控制等,以满足不同的需求。
4.2.2 请求发送与接收响应
发送HTTP请求与接收响应是通过HTTP客户端完成的,常见的HTTP客户端有命令行工具(如curl),以及编程语言中提供的HTTP库。
使用curl命令行工具发送HTTP请求的示例:
curl -X POST "https://aip.baidubce.com/rest/2.0/face/v3/detect?access_token=您的Access_Token" -d '{"image":"您的Base64编码图像数据"}' -H "Content-Type: application/json"
在这个curl命令中: - -X POST 指定了HTTP方法为POST。 - "https://aip.baidubce.com/rest/2.0/face/v3/detect?access_token=您的Access_Token" 是请求的URL,包含API端点和access_token参数。 - -d 参数用于指定HTTP POST请求发送的数据,这里是编码后的图像数据。 - -H 参数用于指定HTTP头信息,这里是内容类型为JSON。
执行完上述命令后,服务器将返回响应。响应通常包括状态码、响应头以及响应体。响应体中包含了人脸检测的结果,通常是JSON格式的数据。
以curl命令为例,了解手动发送HTTP请求的步骤和如何处理响应,能够帮助开发者更好地理解和调试API调用过程中出现的问题,提高开发效率和问题解决速度。
5. 请求参数的准备与发送
在使用百度AI平台的API进行人脸识别时,准确、有效地构建和发送请求参数是成功调用API的关键。本章将深入探讨如何构建API请求的参数,并在不同场景下进行实际应用。
5.1 构建有效的API请求参数
5.1.1 参数格式要求
当与API进行交互时,确保所有参数均遵循API文档中定义的格式要求是至关重要的。通常,这些参数需要以键值对(key-value pairs)的形式通过HTTP请求发送。每项参数都有其特定的数据类型和范围限制。例如,一些参数可能需要是整数,而另一些可能需要是字符串。同时,还需注意参数的大小写敏感性,以及对于某些参数是否存在特殊字符限制。
在使用百度AI平台的API时,许多API要求开发者提供API Key和Secret Key来验证请求。此外,API可能还需要提供必要的功能参数,如图像的URL或Base64编码。
示例代码:
// C# 示例代码,构建API请求参数
Dictionary<string, string> parameters = new Dictionary<string, string>();
parameters.Add("access_token", accessToken); // API密钥
parameters.Add("image", Convert.ToBase64String(File.ReadAllBytes("path/to/image.jpg"))); // 图像文件
5.1.2 参数加密和签名过程
由于安全性原因,一些API要求对参数进行加密,并在请求时附加签名以验证请求的合法性。参数的加密过程确保了数据在传输过程中的安全。签名通常通过API提供的密钥对参数进行加密,生成一个签名字符串,然后将这个签名附加在请求中一起发送给服务器。
在.NET中,可以使用以下逻辑来生成签名:
// C# 示例代码,生成请求签名
string secretKey = "your_secret_key"; // API密钥
string baseString = "access_token=" + accessToken + "&image=" + imageParameter; // 基础字符串
// 使用HMACSHA256加密算法来生成签名
using (HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey)))
{
byte[] signatureBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(baseString));
string signature = Convert.ToHexString(signatureBytes); // 签名值
// 将签名值附加到请求参数中
}
5.2 多种场景下的参数应用
5.2.1 常规用户认证的参数准备
用户认证是API交互中常见的需求,例如,需要验证调用者是否有权限使用API。API Key和Secret Key通常用于这种场景下的认证。在.NET环境中,可以创建一个认证模块,用于生成符合API要求的参数。
// C# 示例代码,生成常规认证参数
public static Dictionary<string, string> GenerateAuthenticationParameters(string apiKey, string secretKey)
{
// 示例:生成access_token参数
// 实际操作中,可能需要调用专门的接口来获取access_token
Dictionary<string, string> authParams = new Dictionary<string, string>();
authParams.Add("access_token", apiKey);
return authParams;
}
5.2.2 高级应用:自定义参数设置
在实际应用中,根据不同的业务场景,开发者可能需要设置一些自定义参数。例如,设置识别的类型(人脸检测、人脸识别等),或对返回结果的格式进行定制。
// C# 示例代码,设置高级自定义参数
public static void SetAdvancedParameters(ref Dictionary<string, string> parameters)
{
// 设置检测类型为静态人脸检测
parameters.Add("face_type", "STANDARD");
// 设置返回结果的详细程度
parameters.Add("output_type", "simple");
}
在发送请求时,将这些参数添加到请求的参数集合中:
// C# 示例代码,发送带有自定义参数的请求
Dictionary<string, string> advancedParams = new Dictionary<string, string>();
SetAdvancedParameters(ref advancedParams);
// 将自定义参数与认证参数合并
foreach (var param in advancedParams)
{
parameters.Add(param.Key, param.Value);
}
// 接下来的步骤是发送HTTP请求并处理响应...
在本章节中,我们学习了如何构建有效的API请求参数,并针对不同场景准备和应用这些参数。在实际操作中,开发者应依据API提供者的文档进行参数的准备,以确保API能够正常响应。在下一章中,我们将探讨如何解析服务器响应的数据,并将其应用在实际的业务逻辑中。
6. 服务器响应数据的解析与应用
在前一章节中,我们深入了解了如何构建API请求参数,并通过不同的场景来演示参数的具体应用。在本章中,我们将聚焦于服务器响应数据的解析与应用。这一环节对于整个应用程序来说至关重要,因为无论API调用是否成功,最终都需要对返回的数据进行解析并加以利用。
6.1 JSON数据解析技术
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。JSON已经成为数据交换的标准格式之一,特别是在Web API的响应中。.NET环境下有多种优秀的JSON解析库,比如Json.NET(Newtonsoft.Json),它提供了丰富的功能来处理JSON数据。
6.1.1 JSON数据格式概述
JSON数据由键值对组成,数据在键和值之间通过冒号 : 分隔,不同的键值对之间通过逗号 , 分隔。对象由花括号 {} 包围,数组由方括号 [] 包围。JSON数据可以嵌套,即值可以是数组或对象。例如,一个典型的JSON对象可能如下所示:
{
"name": "John Doe",
"age": 30,
"isEmployed": true,
"address": {
"street": "123 Main St",
"city": "Anytown"
},
"phoneNumbers": [
{"type": "home", "number": "212 555-1234"},
{"type": "office", "number": "646 555-4567"}
]
}
6.1.2 .NET环境下JSON解析器使用
在.NET中,使用Json.NET库来解析JSON数据是十分常见的做法。首先需要安装这个库,可以通过NuGet包管理器安装:
Install-Package Newtonsoft.Json
然后,可以使用以下代码来解析JSON数据:
using Newtonsoft.Json;
// 假设 responseText 是从服务器接收到的JSON字符串
string responseText = @"{
'name': 'John Doe',
'age': 30,
'isEmployed': true,
'address': {
'street': '123 Main St',
'city': 'Anytown'
},
'phoneNumbers': [
{'type': 'home', 'number': '212 555-1234'},
{'type': 'office', 'number': '646 555-4567'}
]
}";
var person = JsonConvert.DeserializeObject<dynamic>(responseText);
// 现在可以访问解析后的数据
Console.WriteLine(person.name); // 输出: John Doe
Console.WriteLine(person.age); // 输出: 30
在上述代码中, JsonConvert.DeserializeObject<dynamic> 方法被用来将JSON字符串反序列化为一个动态类型的对象。这是在你事先不知道JSON结构时一个很好的选择。当然,如果JSON结构是已知的,建议定义一个强类型的类,并使用 JsonConvert.DeserializeObject<YourClass>(jsonString) 来进行反序列化。
6.2 响应数据的处理和应用
服务器的响应数据包含了API调用的结果,可能包含成功信息、错误信息或实际的数据。这些数据需要被适当地处理并应用到应用程序中。
6.2.1 结构化数据的提取
在解析JSON之后,通常需要从中提取出具体的数据以供应用程序使用。以下是一些示例代码,展示了如何在.NET环境下处理JSON数据:
// 假设 responseText 是从服务器接收到的JSON字符串
string responseText = @"{
'status': 'success',
'message': 'Face detection completed',
'data': {
'faceId': 'abc123',
'人脸位置': {
'left': 10,
'top': 20,
'width': 150,
'height': 150
}
}
}";
var response = JsonConvert.DeserializeObject<dynamic>(responseText);
if (response.status == "success")
{
Console.WriteLine($"Face ID: {response.data.faceId}");
Console.WriteLine($"Face Location: {response.data.人脸位置.left},{response.data.人脸位置.top}");
}
else
{
Console.WriteLine($"Error: {response.message}");
}
6.2.2 错误信息的捕获和处理
API调用可能因各种原因失败,因此在处理响应数据时,总是需要准备好处理错误。通常,错误信息会包含在响应数据中,开发者需要通过解析这些信息来了解错误的详情,并据此进行相应的处理。例如:
try
{
// 尝试解析JSON并提取数据
// 如果解析失败或JSON格式不正确,将抛出异常
}
catch (JsonException ex)
{
// 处理JSON解析异常
Console.WriteLine("JSON解析失败: " + ex.Message);
}
catch (Exception ex)
{
// 处理其他类型的异常
Console.WriteLine("API调用失败: " + ex.Message);
}
在实际的应用程序中,错误处理机制需要更加完善。应当对可能的API错误进行分类,并定义相应的处理策略。此外,也应当记录错误日志以便后续的分析和调试。
在下一章中,我们将探讨错误处理与程序健壮性,确保应用程序能够稳定运行并妥善处理各种异常情况。
7. 错误处理与程序健壮性
在任何软件项目中,错误处理和程序健壮性都是至关重要的。通过有效地处理错误和异常,开发者可以确保应用在面对错误或异常情况时仍能保持稳定运行,并提供有用的反馈。让我们深入探讨如何在使用百度AI平台进行人脸识别时,增强程序的健壮性。
7.1 程序异常和错误的分类
7.1.1 API调用常见错误
在使用百度人脸识别API时,我们可能会遇到如下几类常见的错误:
- 网络错误 :由于网络问题导致的API调用失败,可能包括超时、连接被拒绝等。
- 参数错误 :请求参数不符合要求,例如缺少必要参数、参数类型错误或者参数值不在允许的范围内。
- API限制 :API使用频率超过限制或者请求的API服务未被授权。
- 服务端错误 :百度AI平台服务端出现错误,返回了非预期的响应。
// 示例:错误处理代码块(C#)
try
{
// API 调用代码
}
catch (WebException ex)
{
// 处理网络错误
// ex.Status 会提供网络错误的详细信息
}
catch (ArgumentException ex)
{
// 处理参数错误
}
catch (Exception ex)
{
// 处理其他类型的错误
// 打印错误堆栈信息,便于调试
Console.WriteLine(ex.ToString());
}
7.1.2 网络异常和重试机制
网络异常是应用开发中常见的问题。为了提高程序的健壮性,开发者可以实现重试机制,来应对临时的网络波动或不稳定情况。通过在捕获异常后增加重试逻辑,可以提升用户体验和减少人工干预。
// 示例:带有重试机制的代码块(C#)
const int maxAttempts = 3; // 设置最大尝试次数
for (int attempt = 0; attempt < maxAttempts; attempt++)
{
try
{
// API 调用代码
break; // 成功调用后退出循环
}
catch (WebException ex)
{
// 如果达到最大尝试次数,则抛出异常
if (attempt == maxAttempts - 1)
{
throw;
}
// 等待一段时间后重试
Thread.Sleep(1000);
}
}
7.2 程序健壮性设计
7.2.1 防御性编程技巧
防御性编程是指在编程时考虑到可能出现的错误情况,并为这些情况编写代码。它涉及使用断言、输入验证和异常处理等技术来确保代码在运行时的正确性和鲁棒性。例如,在将数据传递给API之前,我们应该检查这些数据是否满足API的要求。
// 示例:输入验证(C#)
public void ValidateInput(string input)
{
if (string.IsNullOrEmpty(input))
{
throw new ArgumentException("Input cannot be null or empty.");
}
// 其他必要的验证逻辑...
}
7.2.2 系统监控和日志记录
系统监控和日志记录对于错误分析和定位至关重要。使用日志记录框架来记录重要的事件、错误和警告信息。同时,监控系统可以提供实时的性能指标和警告,帮助开发者快速响应问题。
// 示例:日志记录(C#)
// 使用NLog、log4net或其他日志库记录错误信息
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
try
{
// API 调用代码
}
catch (Exception ex)
{
Logger.Error(ex, "Error occurred during API call.");
throw;
}
通过以上策略,开发者可以显著提高程序的健壮性,减少因错误而造成的停机时间。在第七章中,我们将进一步探讨如何使程序在不同场景下表现出更好的稳定性和可靠性。
简介:本项目将介绍如何在.NET开发环境中实现人脸识别,利用百度AI平台提供的API完成人脸检测、比对、搜索等功能。内容包括创建应用获取API凭证,调用百度提供的.NET SDK,以及手动实现HTTP请求,处理服务器响应,并确保调用的安全性和性能优化。项目细节将取决于开发者具体实现。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)