代码改变世界

[翻译]用.NET Compact 开发Facebook

  onm  阅读(379)  评论(0编辑  收藏  举报

概要

Facebook在社交网络上已经成为了一个巨大的现象。这个站点暴露给了开发者一些API以支持网站和桌面应用开发。在这篇文章里,我们将探索在智能设备应用上使用这些函数。这篇文章附带的源代码显示了Facebook API的关键部分和轻量融合的Microsoft Windows Mobile特殊API的协同工作。(The source code to accompany this article demonstrates working with key aspects of the Facebook API and tightly integrating with Microsoft? Windows Mobile?–specific APIs.)

Facebook开发工具包,包含Facebook Compact类库和相关的例子,位于由微软的分享源代码站点CodePlex,在http://www.codeplex.com/FacebookToolkit.

用于

Microsoft Visual Studio 2005
Microsoft Windows Mobile 5.0
Microsoft Windows Mobile 6

介绍

Facebook是一个近期非常流行的社交网站。Facebook暴露了丰富的API,提供开发者构建融入Facebook站点的网络应用,还有可以访问Facebook内容的桌面应用。这篇文章将研究后一个途径,Facebook怎样被用于.NET Compact Framework来构建独立的智能设备应用。

Facebook开发工具包

Facebook是一个因特网服务,并且暴露了基于API的网络服务。代替了简单对象访问协议(SOAP)网络服务和很多托管代码开发者相似,Facebook用了表象化状态转变(和这个服务REST)API。因为你不能用Visual Studio自动创建的代码来相互作用,你需要写函数来请求和解析回复。幸运的是,这些已经在Facebook开发者工具包中做好了。这个桌面.NET类库暴露了简单的模型。这个工程被建立作为微软Visual Studio Express展示入门开发包。在现在的发布版本中,它不支持.NET Compact Framework。这个工程在CodePlex上(www.codeplex.com),微软共享源代码站点。当我开始感兴趣添加对Compact Framework支持,我加入了CodePlex工程并且开始添加修改。

支持Compact Framework

我在解决方案里建立了一个分开的工程,叫做Facebook.Compact进入到这里,我加入了所有存在的文件作为引用到存在的代码里。这样可以确保仍然有一套源代码,这样的改变使得桌面工程将被影响到设备版本。我添加了一个编译版本的NETCF到这个工程里;(This ensures that there is still only one set of source code so changes made in the desktop project will be reflected in the device version. I added a compilation constant of NETCF to the project;)这允许我添加额外的代码段,我需要从Compact Framework中隐藏的,因为他们使用的功能是不可见的。例如,桌面组件使用大量的属性是不在Compact Framework运行时的:

1
namespace Facebook.Components{#if !NETCF    [ToolboxItem(true), ToolboxBitmap(typeof(FacebookService))]    [Designer(typeof(FacebookServiceDesigner))]#endif    public partial class FacebookService : Component    {

这个设计时属性被代替应用到这个组件靠一个.XMTA的文件。靠隐藏一些设计时无关的属性提高设计体验并且增加了一些细节:

1
<?xml version="1.0" encoding="utf-16"?><Classes xmlns="http://schemas.microsoft.com/VisualStudio/2004/03/SmartDevices/XMTA.xsd">  <Class Name="Facebook.Components.FacebookService">    <Property Name="ApplicationKey">      <Category>Facebook</Category>      <Description>Access Key required to use the Api</Description>    </Property>    <Property Name="UserId">      <Browsable>false</Browsable>    </Property>    <Property Name="Secret">      <Category>Facebook</Category>      <Description>Secret Word</Description>    </Property>    <Property Name="SessionKey">      <Browsable>false</Browsable>    </Property>    <Property Name="SessionExpires">      <Browsable>false</Browsable>    </Property>    <Property Name="LoginUrl">      <Browsable>false</Browsable>    </Property>    <Property Name="LogOffUrl">      <Browsable>false</Browsable>    </Property>    <Property Name="IsDesktopApplication">      <Browsable>false</Browsable>    </Property>  </Class></Classes>

我也不得不写一些额外的代码来添加桌面版本的功能。主要来支持URL编码的字符串。这就是特殊字符被替换的地方,以便字符串可以被安全的作为URL请求或表单邮寄的一部分发送。另一个附加的基本的措施是WebClient.DownloadData用HttpWebRequest来封装取回反映并且返回这个原始字节。当编写一个桌面(或者在这种设备)应用时,唯一交互的部分不能通过原始认证的API来做。它必须通过网页来传递你的应用ID和显示一个相似的登录界面给用户。对于.NET Compact Framework通过一个专注的表格来承载WebBrower控件来实现。这提供了一个完整的登录方案,所以我们不必在微软的IE Mobile上来演示登录并且切换我们的应用到后台。不幸的是,没有一个设备的版本的登录界面,所以他的表现没想象中的好。下面的图1展示了这个在Windows Mobile设备上运行的登录界面。

Facebook API Load Page

图1. Facebook API登录页

一旦用户成功进行身份验证的登录表单被自动关闭(Once the user has successfully authenticated the login form is automatically dismissed)。我们检查控制导航到的页面URL。对于不是在网站上的应用,将直接跳转到desktopapp.phpwhich 告知用户关闭窗口并且返回他们的应用。对于左面和便捷设备使用的是同样的登陆页。如下是在WebBrowser控件的Navigated事件处理函数中的代码:

1
private void wbFacebookLogin_Navigated(object sender, WebBrowserNavigatedEventArgs e){if (e.Url.PathAndQuery.IndexOf("desktopapp.php") > -1){DialogResult = DialogResult.OK;}}

所有这些我做的对于代码的修改不代表现在Facebook开发工具包中的安装包,所以你不需要下载源码中的包。它将提供给你解决方案并且所有库的源代码。你可你下载最新代码在:http://www.codeplex.com/FacebookToolkit/SourceControl/ListDownloadableCommits.aspx 解压这个.ZIP文件到我的文档中的一个目录。在解决方案中有一个目录叫做Device Samples,包含了我们通过这篇文章将提到的示例应用。

注册一个Facebook应用

在你能创建你自己的Facebook应用前,你需要注册生成一个唯一的API密钥和secret phrase。这个第一步是添加开发者应用到你的Facebook概述中。一旦你已经添加了这个应用,去应用页(http://www.facebook.com/developers/) 并且点击了开始新应用按钮,如图2所示。你可以稍后继续为你的应用提供一个唯一的名字。

图2 Facebook开发者主页

下一个包含所有的配置选项为你的新应用。图3显示了一个怎样填写的例子。你需要通过这个过程来得到一个API密钥,你可以按照如下例子使用。

图3.新应用页

一旦你已经创建了一个应用,你可以游览你的应用ID和secret phrase,通过我的应用页,通过开发者应用主页很容易进入。图4显示了我的应用页。你的API密钥和secret phrase将被在中间的空白区域显示。你需要这些来从你的应用登录Facebook。

图4. 我的应用页

你现在已经有了全部你的Facebook需要验证的东西。一个API密钥和secret phrase是应用所需要的,我们需要安全的存储他们在设备里。

存储你的密钥

有很多种方法来存储API密钥和Secret phrase值到你的应用里。他们都是包含十六进制数据的字符串。这个桌面应用的例子把他们存储到一个配置文件里。为了方便,在示例应用中,我们把这些值写到设备的注册表中。对于Release的版本你不能这样做,因为他们对于注册表编辑器是不加修饰的文本。为了使用下面的示例应用,你需要添加下列的注册表键值到设备中。你可以使用Visual Studio附带的Remote Registry Editor工具。

1
HKEY_LOCAL_MACHINE\SOFTWARE\InTheHand\FacebookDeviceSampleApplicationKey (String) = Your API KeySecret (String) = Your Secret

下列的所有示例应用都将从这个注册位置加载该值。

根证书

Facebook认证使用HTTPS communication来保证安全。然而,在Windows Mobile上,这个会引发一个问题,因为Facebook使用的根证书在设备上默认是不被允许的。这意味着安全的HTTPS请求将会失败。幸运的是可以靠下载根证书和添加到设备这个方法来变通。

关于根证书的列表的信息你可你在http://www.geotrust.com/resources/root_certificates/index.asp找到。由于这个列表相当长,你可能相当为难。你需要找出叫“Root 5 - Equifax Secure Global eBusiness CA-1.” 的入口。你将找到下载链接并且存为.CER文件。这个认证对于DER编码的和Base-64编码的都是可用的。如果你是用Windows Mobile 5.0确保你选择DER编码的版本。对于Windows Mobile 6你可以用任意一个版本。https://www.geotrust.com/resources/root_certificates/certificates/Equifax_Secure_Global_eBusiness_CA-1_DER.cer

右击并且存储文件到你的计算机上,却保存为一个.CER后缀的文件。拷贝这个文件到你的设备,用设备上的文件浏览器定位到,并且点击这个文件来安装它。你可以在你的部署文件中包含这个自动进程并且以编程方式让它运行。

Facebook好友

现在我们有了示例的所有先决条件,让我们继续我们的第一个示例应用。第一件我们考虑的事是像Facebook的社交网站能保持一份联系列表。我们看的第一个示例将获得我们的好友列表,它叫做DeviceFriendsSample。

我们已经添加了FacebookServie组件到应用表单,他提供和Facebook API的所有交互。首先的任务是分配API密钥和secret phrase到控件并且建立到Facebook的链接。

1
private void FacebookFriends_Load(object sender, EventArgs e){      //connect to facebook      facebookService1.ApplicationKey =            (string)Microsoft.Win32.Registry.GetValue(       "HKEY_LOCAL_MACHINE\\Software\\InTheHand\\FacebookDeviceSample",            "ApplicationKey", "");      facebookService1.Secret =            (string)Microsoft.Win32.Registry.GetValue(      "HKEY_LOCAL_MACHINE\\Software\\InTheHand\\FacebookDeviceSample",            "Secret", "");      facebookService1.ConnectToFacebook();      RefreshFriendsList();}

RefreshFriendsList方法可以获得用户好友,并且把他们加载到ListView控件。为了减少屏幕刷新,我们调用ListView的BeginUpdate和EndUpdate来更新列表。作为每个用户,带有Name和PictureSmall属性(被绑定到ListView的ImageList一个图片)的ListViewItem被展示。( As each User is read we populate the ListViewItem with theName and assign thePictureSmall property, which is an Image, to the ImageListassociated with the ListViewcontrol.)

1
private void RefreshFriendsList(){      lvFriends.Items.Clear();      ilFriends.Images.Clear();      Cursor.Current = Cursors.WaitCursor;      lvFriends.BeginUpdate();      Collection<User> friends = facebookService1.GetFriends();      int i = 0;      foreach (User u in friends)      {            ListViewItem lvi = new ListViewItem(u.Name);            lvi.Tag = u;            lvi.ImageIndex = i;            ilFriends.Images.Add(u.PictureSmall);            i++;            lvFriends.Items.Add(lvi);      }      lvFriends.EndUpdate();      Cursor.Current = Cursors.Default;}

有用的网页

当使用桌面或者网络应用的Facebook API,有大量可以带你去Facebook特殊页面的标准链接。虽然这需要一个活动的网络连接,它提供了能够看到的属性和执行操作,无法通过Facebook的API本身提供。这些URL有同样等值的更好格式的设备应用版本。全部手机格式化的URL在表格1中。

表格1 通用Facebook页URL

URL Purpose
http://m.facebook.com/profile.php?id=xxxxx Display a specific user profile.
http://m.facebook.com/event.php?eid=xxxxx Display a specific event.
http://m.facebook.com/group.php?gid=xxxxx Display a specific group.
http://m.facebook.com/poke.php?id=xxxxx Poke the specific user.
http://m.facebook.com/message.php?id=xxxxx &subject=xxxxx&msg=xxxxx Send the specified user a message.
http://m.facebook.com/photo_search.php?id=xxxxx View all photos of a specific user.
http://m.facebook.com/wall.php?id=xxxxx Read the user’s wall.
http://m.facebook.com/notes.php?id=xxxxx  

在我们好友的例子中,我使用这些URL的第一个。在第一个软键(Soft key)我们有一个View menu item。当点开一个Mobile IE窗口时,打开特别的用户。你也可以使用WebBrowser控件和你的应用集成。(You could also use aWebBrowser control hosted within your application to remain more integrated.)这里的一个限制是使用网站需要一个分开的登录,但是为用户存储登录是可行的,以便很长时间没使用后他们会被提示。登录一个特别的URL需要一个Process.Start的单独调用。

1
System.Diagnostics.Process.Start("http://m.facebook.com/profile.php?id=" + u.UserId, "");

图5显示了一个关联配置在Interner Explorer Mobile。

图5.配置页

同步

就现在我们所知,由于隐私原因,一些用户信息的关键部分没有通过API暴露。然而,如果我们能同步用户图片到Outlook Moblie是有用的,所以他们能被即将到来的呼叫屏幕和短信窗口使用。我们可以使用托管的Pocket Outlook类来实现,在Microsoft.WindowsMobile.PocketOutlook命名空间。在DeviceFriendsSample应用的菜单中,我们有一个同步入口。当被点击时,我们开始通过好友和检查用户地址簿的联系人匹配来枚举。当一个联系人被发现名和字(名字)匹配时,我们打开联系项来编辑。如果没有匹配的联系人被发现,我们创建一个新的联系人项。为了把图片分配给联系人,我们必须首先存储图片到一个我们能够立即删除的临时文件。Microsoft Office Outlook Mobile在内部存储这些图片,所以除了同步过程,我们不需要占据明确的存储空间。很明显,下载大品质图片将使用更多网络带宽,所以当设备连接到可支付的网络时,使用通用尺寸。以下代码段显示出了同步代码:

1
private void mnuSync_Click(object sender, EventArgs e){   foreach (ListViewItem lvi in lvFriends.Items)   {      Contact c = null;      User u = (User)lvi.Tag;      //lookup the user in poom      ContactCollection matches =         session.Contacts.Items.Restrict("[FirstName] = \"" + u.FirstName         + "\" AND [LastName] = \"" + u.LastName + "\"");      if (matches != null && matches.Count > 0)      {         c = matches[0];      }      else      {         //add a new contact         c = session.Contacts.Items.AddNew();         c.FirstName = u.FirstName;         c.LastName = u.LastName;         //mark this as a facebook contact         c.Categories = "Facebook";      }      //create temp filename      string filename =         System.IO.Path.Combine(System.IO.Path.GetTempPath(),         u.UserId + ".jpg");      //delete if it exists      if (System.IO.File.Exists(filename))      {         System.IO.File.Delete(filename);      }      //save and assign image      u.PictureBig.Save(filename,         System.Drawing.Imaging.ImageFormat.Jpeg);      c.SetPicture(filename);      //delete temp file (outlook stores a copy)      System.IO.File.Delete(filename);      //store userid for future reference      c.Properties.Add("Facebook.UserId", typeof(string));      c.Properties["Facebook.UserId"] = u.UserId;      //save the contact      c.Update();   }}

处理事件

Facebook提供用户创建日历事件,你可以为任何目的创建。无疑有一些.NET事件和更多的一些在Outlook Mobile的日历约会事件。你可以邀请朋友或者创建任何人都能注册参与的公共事件。通过Facebook API,你可以得到关于你被邀请(或者已经创建)的事件的详细信息。你也可以观看当前的参与者(出席,不出席,等等)数量。你不能通过Facebook API创建或者修改事件。我们的下一个示例应用,DeviceEventSample,将展示怎样获得事件的详细信息。我们显示从Facebook返回的事件列表,显示到ListView空间里,包含事件的图片。屏幕的如图6所示。因为事件图片不是特殊的宽高比,一些拉伸会在示例中发生。这个解决方案使用了交替的控件来显示,像是一个owner-drawn列表控件,或者复制图片到与之大小的canvas来保证所有图片有相同的比例。因为这个示例被设计来展示Facebook API,我们把它留给读者,当作练习来想出一个更好的用户界面。

图6.Facebook 事件

我们可以看见处理FacebookEvent项很容易。这是很酷的事情之一,你可以处理信息到Outlook Mobile来在你的日历中存储事件的副本。这提供你使用所有通常的措施来提醒并且,在你的今天屏幕上显示即将到来的事件。我已经添加了一个关联菜单到这个窗体并且把关联菜单和关联菜单在ListView空间的属相关联。当关联菜单被点击时,我们首先检查当前被选中的项,然后运行AddToCalendar方法:

1
private void AddToCalendar(){      foreach (int i in lvEvents.SelectedIndices)      {            if (i > -1)            {                  Appointment eventAppointment = null;                  FacebookEvent fe = (FacebookEvent)lvEvents.Items[i].Tag;                  Collection<EventUser> attendees =                        facebookService1.GetEventMembers(fe.EventId);                  //check if it exists - if so update it                  AppointmentCollection appointments =                        os.Appointments.Items.Restrict("[Categories] = \"Facebook\"");                  foreach (Appointment a in appointments)                  {                        if ((string)a.Properties["Facebook.EventId"] == fe.EventId)                        {                              eventAppointment = a;                              break;                        }                  }                  //if not found add a new entry                  if(eventAppointment == null)                  {                        eventAppointment = os.Appointments.Items.AddNew();                        eventAppointment.Categories = "Facebook";                        //add the event id in a custom property                    eventAppointment.Properties.Add("Facebook.EventId",                            typeof(string));                        eventAppointment.Properties["Facebook.EventId"] = fe.EventId;                  }                  eventAppointment.Subject = fe.Name;                  eventAppointment.Location = fe.Location;                  eventAppointment.Body = fe.Description;                  eventAppointment.Start = fe.StartDate;                  eventAppointment.End = fe.EndDate;                  RecipientCollection rc = eventAppointment.Recipients;                  //clear current list                  for (int irecipient = rc.Count-1; irecipient > -1; irecipient--)                  {                        rc.Remove(irecipient);                  }                  foreach (EventUser eu in attendees)                  {                        if (eu.Attending == RSVPStatus.Attending)                        {                              //add recipient entries (no valid email addresses)                              Recipient r = rc.Add(new Recipient(eu.User.Name,                                    "facebook"));                        }                  }                  eventAppointment.Update();            }      }}

同样,设置一些标准的属性在Appointment有一些值得附加的解释项。首先是你注意到我们已经把 “Facebook”目录全部自动创建了项。这使得它更容在日历中搜索存在的项是Facebook项。(As well as setting a few of the standard properties on anAppointment there are a couple of items that deserve additional explanation. The first is that you’ll notice we have assigned the “Facebook” category to all programmatically created items. This makes it easier to search for existing items in the calendar that are Facebook items. )接下来,我们利用了Pocket Outlook API在Windows Mobile 5.0的特性,并且我们给项添加了自定义属性。我们可以使用这个技巧来附加事件ID从Facebook到项。我们把这个属性叫做“Facebook.EventId” 来保证他的唯一性从而令人信服的用于其他目的。最后我们利用了Outlook Mobile的会议项支持。我们刷新会议接收方集合,附带所有事件确认参与者的名字。这仅仅是为了显示。这些不能被传统Outlook接收方来一个会议(你可以更新会议和确认参与者),因为Facebook API没有暴露e-mail地址。它使用了一个完全便捷的方式存储存在的约会对话的参与者。一旦事件被创建,它将和桌面的Outlook同步,并且用户可以按照期望处理这些项,例如红外线或者蓝牙,或者设置一个提醒。图7显示在日历应用中创建的约会项。

图7.在日历中的Facebook事件

处理图片

Facebook提供给用户创建多样的图片相册并且每个相册可以上传60张图片。它也提供给你可以在图片上添加标签,来显示给你的朋友看。有一些操作是通过Facebook API无法实现的,你不能删除或者更改相册或者图片。但你添加新图片,它对于其他用户是不可见的。你必须从Facebook网站上激活它,它才能正常的在你的相册中显示。这个实例应用对于游览和管理你自己的图片相册这个区域是一个工具。应用的主窗口是一个ListView空间,来显示每个相册的图片的缩略图。图8显示了打开一些相册的窗体。

图8.Facebook图片

当用户选择一个相册,我们打开一个新的窗体显示这个相册的所有图片。从这个主窗体,你可以添加一个相册并且可以浏览当前相册的所有属性——名字,地点和描述。点击新相册打开一个新的AlbumPropertiesForm实例,你可以填写相册信息。当这个窗体关闭后(点击OK),API被调用来创建一个新的相册。

1
private void AlbumPropertiesForm_Closing(object sender, CancelEventArgs e){      if (isnew)      {            if(!string.IsNullOrEmpty(txtName.Text))            {                  Album newAlbum = parent.facebookService1.CreateAlbum(                        txtName.Text, txtLocation.Text, txtDescription.Text);            }      }}

当用户返回主窗体,我们刷新相册列表。但一个相册创建后,他立即会出现在用户的相册列表中,即使它不包含图片。

当一个相册被选择时,我们打开一个AlbumForm的新实例传递相册详细信息。AlbumForm被设计到主窗体以相似的方式,为了方便,我们使用内建的ListView控件。图片被加载在Load事件处理函数中:

1
private void AlbumForm_Load(object sender, EventArgs e){      Cursor.Current = Cursors.WaitCursor;      lvAlbum.BeginUpdate();      Collection<Photo> photos =            parentForm.facebookService1.GetPhotos(album.AlbumId);      int i = 0;      foreach (Photo p in photos)      {            ListViewItem lvi = new ListViewItem(p.Caption);            lvi.ImageIndex = i;            ilAlbum.Images.Add(p.PictureSmall);            lvAlbum.Items.Add(lvi);            i++;      }      lvAlbum.EndUpdate();      Cursor.Current = Cursors.Default;}

还是图片的宽高比可能变化,所以这里呈现的ListView和ImageList结果中有一些拉伸的图片。然而,它提供了一个快速相册内容显示。图9显示了典型的相册样子。

图9.相册窗体

这个窗体有一个菜单项,你可以上传新的图片到相册中。像创建一个新的相册,新上传的图片仍然不可见直到你通过Facebook网站验证。但你看见网站上的相册显示一些待定项,列出这些等待赞同的项。你不能通过手机版本的网站批准图片。为了结合照相,我们使用了Microsoft.WindowsMobile.Froms组件的CameraCaptureDialog窗体。

1
private void mnuPhoto_Click(object sender, EventArgs e){      Microsoft.WindowsMobile.Forms.CameraCaptureDialog ccd =            new Microsoft.WindowsMobile.Forms.CameraCaptureDialog();      ccd.Mode = Microsoft.WindowsMobile.Forms.CameraCaptureMode.Still;      ccd.Title = "Add photo to Facebook";      ccd.Resolution = new Size(640, 480);      //if success      if (ccd.ShowDialog() == DialogResult.OK)      {            System.IO.FileInfo fiImage = new System.IO.FileInfo(ccd.FileName);            parentForm.facebookService1.UploadPhoto(album.AlbumId, fiImage);      }}

UploadPhoto方法需要albumid和包含图片文件详细信息的FileInfo。FileInfo构造函数需要通过从camera对话框来的文件路径信息。

总结

在这篇文章的这些示例中探索了怎样使用Facebook开发者工具包暴露出来的函数。尽管Facebook没有通过API暴露全部的网站功能,还是提供了很大范围的方法来获取你的好友、组织、事件、图片信息。我们也了解了怎样利用Windows Mobile APIs来集成Facebook数据到我们设备的联系人和日历应用中来。最终,我们了解了怎样通过你的手持设备直接浏览和上传你的在线图片相册。

参见

最近Facebook的发展已经产生了许多的反响(buzz)。一个有意思的例子是叫做OutSync的桌面应用。它协同桌面Outlook同步Facebook配置你联系人的图片,然后可以跟Windows Mobile device同步。这个和我们上面创建的东西概念上有一些相似。另外,它使用了WPF用户界面。在Channel 10上一个展示该产品的视频:http://www.on10.net/Blogs/laura/sync-your-facebook-contacts-with-outlook-and-windows-mobile/

作者介绍

Peter Foot is the founder of In The Hand Limited, a company providing software development and consulting services for mobile devices. In The Hand also produces award-winning software components for the .NET Compact Framework to assist other developers. In 2007 Peter co-authored the Microsoft Mobile Development Handbook published by Microsoft Press?.Peter established the 32feet.NET shared-source community project bringing Bluetooth and IrDA technologies into easy reach of .NET developers. Peter has also been an active contributor to several other shared-source initiatives.Prior to working with Windows Mobile, Peter led a test team for a mobile Internet portal in Great Britain on the first consumer GPRS service in Great Britain. Peter holds a BSc in Computer Science.

原文地址:http://msdn.microsoft.com/en-us/library/bb932386(v=MSDN.10).aspx
12/28/2009
Peter Foot, In The Hand Ltd
November 2007
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述
点击右上角即可分享
微信分享提示