Facebook has opened up it's Social website to third-party developers http://developers.facebook.com/
Additionally they have partnered with Microsoft to make developing applications extremely simple
http://msdn.microsoft.com/vstudio/express/showcase/
Last night I decided to take this Facebook toolkit for a spin, by first converting a very simple Windows Presentation Foundation (WPF) application I'd written to automatically synchronize your My Pictures and Shared Pictures folders with Facebook.
The below process is what I did.
- Download the facebook toolkit from the Microsoft link above.
- Create your project type per normal. (WPF/Website etc!) I used WPF in this example
- Add the facebook/facebook.csproj project to your solution (yes you can add just a reference to the facebook.dll also if you wish, but i didn't for reasons below).
Once you've done the above it's as simple as adding:
1: using Facebook;
2: using Facebook.Components;
3: FacebookService fbService;
4:
5: fbService = new FacebookService();
6: fbService.IsDesktopApplication = true; // set this to true for desktop applications.
7:
8: fbService.ApplicationKey = "[your key here]";
9: fbService.Secret = "[your secret here]";
10:
11: Collection<Album> tAlbums = fbService.GetPhotoAlbums(); // Make the call to a function you wish to use
12:
If your not already logged in it will automatically popup a window like the following:
The login screen is automatic, and the function you called while the user was not logged in will not return until the user has successfully logged in or cancelled the login process.
Very easily done.
Next once I've gotten all of the albums it's a simple matter to get all the photo's using:
1: Collection<Photo> tPhotos;
2:
3: foreach (Album ta in tAlbums)
4: { 5: fbAlbums.Add(ta.Name, ta);
6: if (getPhotos)
7: { 8: tPhotos = fbService.GetPhotos(ta.AlbumId); // gets all of the photo's for the album.
9: fbPhotos.Add(ta.Name, tPhotos); // global Dictionary<string,Collection<Photo>> where i store album --> photo reference.
10: }
11: }
Finally to figure out which Photo's I want to upload I scan the folders "My Pictures" and "Shared Pictures" using the following methods to get the directories:
1: // Shared Pictures folder path
2: // You might get "C:\Documents and Settings\All Users\Documents\My Pictures"
3: // Here, "My Pictures" is the real name of the folder "Shared Pictures", where "Shared Pictures"
4: // is only the alias of "My Pictures" and can't be retrieved.
5: string shrpic = (string)Registry.GetValue(key.Name, "CommonPictures", "");
6:
7: lbDirectories.Items.Add(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)));
8:
9: if (shrpic != null && shrpic.Length > 0)
10: { 11: lbDirectories.Items.Add(new DirectoryInfo(shrpic));
12: }
13:
Then using a simple recursive function to get the list of picture files (not shown here for brevity).
Now the next step is to compare what pictures I have on the local machine to what's one the server so I do:
Oops Facebook doesn't store the original filename!! So there technically is no way to figure out which local pictures match which server pictures.
This was a major stumbling block, as I originally didn't want to have to store any information locally on the users computer. Alas you must manually keep track of what photo's you've uploaded previously and what their photoID is (details coming later).
Next what I do is loop through all of the pictures and upload them using:
1: foreach (string aname in dNewImages.Keys)
2: { 3: if (fbAlbums.ContainsKey(aname) == false) // if the album doesn't exist on the server then create it.
4: { 5: fbService.CreateAlbum(aname, "", ""); // create the album
6: getAlbums(false); // refresh my list of albums, You could remove this step by doing something similar to what i do later.
7: }
8: Collection<CFileInfo> aImages = dNewImages[aname]; // list of all the images for this album
9: XmlDocument rv = null;
10: long pid;
11: foreach (CFileInfo f in aImages) // loop through the images.
12: { 13: string tempFile = System.IO.Path.GetTempFileName(); // create a temporary file
14: FileInfo fi = new FileInfo(tempFile);
15: CImageUtils.resizeImageAndSave(f.File.FullName,tempFile,604,453); // resize the image
16: rv = fbService.UploadPhoto(fbAlbums[aname].AlbumId,fi); // upload the image to facebook!
17: fi.Delete();
18: pid = FacebookUtils.getUploadedPhotoID(rv); // get the photo id for the new image.
19:
20: dUploadedImages.Add(f.File.FullName,pid.ToString()); // add a mapping of the filename to the photo id.
21:
22: }
23: }
24:
The primary problem with the above is shown in the purple line, if you look at your copy of FacebookService the UploadPhoto does not return a result! So there is no way to determine, the result of the call. We need this result because we need to be able to determine what the new Photo's ID is. Now you'll see why I added the Facebook project to my solution, so I could jump into the source code and fix this small issue. If you jump to this function you will see that it calls:
ExecuteApiUpload(uploadFile, parameterList);
This method Does return a result, and the result is an xml document that looks like the following:
1: <?xml version="1.0" encoding="UTF-8" ?>
2: <photos_upload_response xmlns="http://api.facebook.com/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://api.facebook.com/1.0/ http://api.facebook.com/1.0/facebook.xsd">
3: <pid>2958831660596058150</pid>
4: <aid>2958831660595942926</aid>
5: <owner>688906680</owner>
6: <src>http://photos-870.ak.facebook.com/photos-ak-sf2p/v73/140/31/688906680/s688906680_120870_3345.jpg</src>
7: <src_big>http://photos-870.ak.facebook.com/photos-ak-sf2p/v73/140/31/688906680/n688906680_120870_3345.jpg</src_big>
8: <src_small>http://photos-870.ak.facebook.com/photos-ak-sf2p/v73/140/31/688906680/t688906680_120870_3345.jpg</src_small>
9: <link>http://www.facebook.com/photo.php?pid=120870&id=688906680</link>
10: <caption />
11: <created>1181012493</created>
12: </photos_upload_response>
From here you can see that the tag <PID> contains the newly created Photo ID. What we need to do then is modify the UploadPhoto method to return an XmlDocument that we get from the ExecuteApiUpload method call. I changed the function and then wrote the following function to extract the photoid.
1: public static long getUploadedPhotoID(XmlDocument doc)
2: { 3:
4: XmlNamespaceManager xnm = new XmlNamespaceManager(doc.NameTable);
5: xnm.AddNamespace("fb", "http://api.facebook.com/1.0/"); 6: XmlNode node = doc.SelectSingleNode("/fb:photos_upload_response/fb:pid/text()",xnm); 7: return long.Parse(node.Value);
8: }
If you notice here I had to include an XmlNamespaceManager because the default namespace is not blank, otherwise your select will fail.
At this point it's just a simple matter of saving a reference of the FileName --> PID into a Dictionary<string,string> and then saving this to a application state file that we can load the next time we run. This works fine as long as the user always only uses our program to upload files, otherwise it's very easy to get duplicates. Unfortunantly there isn't an easy way around this since facebook doesn't save the original image filename.
Anyway this was just a simple app to try and testout the new facebook api's that microsoft provides. Overall I'd give them a A+ for simplicity of use, however they left out quite a few obvious pieces of required functionality like return codes from the api set. However since they distributed the source it is quite easy to work around that issue.