Live Connect ile SkyDrive'a programatik file upload

0 dakikada yazıldı

37166 defa okundu

Düzenle

Build konferansında SkyDrive'a ait yeni API'lerin açılacağı ve Beta olarak sunulduğu duyurusunun yapıldığı anda hemen aklımda kenara not almıştım, "Ben bu işe bir bakiyim" diye :) SkyDrive malum kullanıcı başına 25GB alan veriyor ve bu çoğu senaryoda özellikle free cloud storage anlamına gelip güzel bir back-up lokasyonu olabiliyor. Özellikle geliştirdiğiniz son kullanıcı ürünlerinde backup işlevselliği için çok uygun bir seçenek.

Örnek senaryo

Şimdi varsayalım ki elimizde bir CMS (Content Management System) var. Bunu müşterilerimize satıyoruz. CMS'in kendi iç backup sistemi, scheduler'ı vs olacaktır. Fakat genelde bu backuplar hep CMS'in bulunduğu sunucuda tutulur ki bu aşırı mantıklı sayılmaz :) Yani sunucunun başına birşey gelse backuplar da gider. Backup'ları alıp scheduled bir şekilde başka bir sunucuya göndermek akıllıca olur ama bu sefer de sunucu masrafları artar, entegrasyon sorunlu olabilir vs vs...

Tüm bu sorunları çözmek adına CMS yönetim panelinde bir bölüm tanımlayıp backup için kullanıcıların LiveID'leri ile SkyDrive hesaplarını CMS'e bağlamalarını isteyebilirsiniz. Böylece CMS artık istediği zaman söz konusu SkyDrive hesabına bağlanığ backupları kopyalar ve herhangi bir sorun olduğunda cloud'da backupımız olduğunu biliriz.

Peki nasıl yapacağız tüm bunları?

SkyDrive'ın yeni SDK'sı ile beraber Windows 8 Metro ve WP7 için bazı yardımcı kütüphaneler geliyor fakat ben bu kütüphaneleri pek beğenmedim şu anda. O nedenle direk REST API'leri sağlayan SkyDrive'ın API'lerin doğrudan ulaşacağız. Ben örnek boyunca bir WPF uygulaması kullanacağım fakat bunu platform spesifik pek birşey kullanmayacağım için ister Metro, ister WP7 tarafına da geçirebilirsiniz.

Live Connect API için Application Tanımı

SkyDrive ile konuşmak için Live Connect API'lerini kullanacağız. Hazırlayacağımız uygulamanın SkyDrive ve LiveConnect sisteminde tanımlı olması lazım ki API'lere ulaşıp, kullanabilelim. Bunun için hemen http://go.microsoft.com/fwlink/?LinkId=193157 adresini ziyaret edebilirsiniz.

Uygulamamızı Live Connect için
tanımlarken...Uygulamamızı Live Connect için tanımlarken...

Siteye gittiğinizde ilk yapmanız gereken uygulamanızı bir isim verip dilini seçmek. Dil kısmı çok kritik değil nitekim uygulama adı da çok kritik değil ama özellikle uygulama adı daha sonraları kullanıcılarınız kendi LiveID'leri ile uygulamanızı bağlarken gösterilecek isim olacak. Bu adımın hemen sonrasında size ClientID ve Secret Key verilecektir.

ClientID'miz ve Secret Key'imiz
karşımızda.ClientID'miz ve Secret Key'imiz karşımızda.

Client ID ve Secret Key çok önemli. Uygulamanız REST API'lerini kullanırken bunlara ihtiyaç duyacak. Bir anlamda bu bilgiler uygulamamızın içine gömülü olacak.

Mobile App seçeneği çok
kritik.Mobile App seçeneği çok kritik.

Web sitesinden çıkmadan son olarak yapmamız gereken bir ayar daha var. HemenAPI Settings bölümüne geçip "Mobile client app" seçeneğini "Yes" olarak değiştirmeniz gerek. Web uygulamalarında bir redirect domain sayfası kullanılarak Login sonrası bu adrese yönlendirme yapılması sağlanabiliyor ama bizimki bir web uygulaması olmadığı için biz böyle özel bir yönlendirme istemiyoruz.

Login ile SkyDrive erişim haklarını almak

LiveID'si olan bir kullanıcının SkyDrive erişim haklarını almak için kesinlikle tarayıcı bazlı bir platform yaratmamız şart. Eğer bir ASP.NET sitesi ise doğrudan SkyDrive login sayfasına yönlendirip bir önceki adımda belirtlemediğimiz "Redirect Domain" bilgisini de sitenizin adresini yazıp konuyu çözebilirsiniz. Eğer bir WPF/Windows uygulaması yazıyorsanız WebBrowser componenti ile kullanıcıya SkyDrive Login sayfasını göstermemiz gerek. Bu işlemi kullanıcının sadece bir defa yapması yeterli olacak. Sonrasında biz artık istediğimiz zaman söz konusu kullanıcının SkyDrive'ına ulaşabileceğiz.

[VB]

    Private ClientID As String = "000XXXXXXXXXXXXXXX03C"\     Private ClientSecret As String = "9dAkN6XXXXXXXXXXXXXXXXXXXXXeCnRI"

İlk olarak sürekli kullanacağımız ClientID ve ClientSecret'ı birer değişken olarak tutalım. Aslında bunları birer uygulama ayarı olarak da saklayabilirsiniz. Karar sizin. (Aslında değişken olmalarına da gerek yok :))

[VB]

  Browser.Navigate(String.Format("https://oauth.live.com/authorize?" &\                                "client_id={0}&scope=wl.offline_access" &\                                "%20wl.skydrive%20wl.skydrive_update&" &\                                "response_type=code&redirect_uri=" &\                                "https://oauth.live.com/desktop", ClientID))

Uygulamamızın ekranında bir WebBrowser componentini Browser adı ile bulundurduğumuzu düşünürsek :) yukarıdaki kod ile kullanıcıyı SkyDrive Login sayfasına yönlendirebiliyoruz. Yönlendirme URL'imiz içerisinde bazı parametreler dikkatinizi çekecektir. Bunlardan ilki tabi ki klasik ClientID'miz :) İkinci parametre ise scope bilgisi. Scope bilgisi ile kullanıcıdan ne tür haklar istediğimizi belirtebiliyoruz. Burada özellikle wl.offline_access çok önemli. Offline erişim istemezseniz her seferinde kullanıcıya login yaptırmak zorunda kalırsınız. Eğer her seferinde kullanıcı login olmak zorunda olmasın ve biz de kafamıza göre tekrar sonrasında kullanıcının SkyDrive'ına ulaşabilelim istiyorsak kesinlikle offline access hakkı istememiz gerek. Örneğimizde istediğimiz diğer haklar wl.skydrive ve wl.skydrive_update ise kullanıcının skydrive'ını okuma ve yazma hakları ile ilgili. Live Connect API'leri aslında kullanıcının takviminden tutun, kontaklarına kadar farklı şeylere ulaşmak için kullanılabiliyor. Biz sadece SkyDrive'a ulaşacağımız için bu hakları istiyoruz. Scope'lar konusunda diğer hakların bir listesineburadan ulaşabilirsiniz.

Kullanıcı SkyDrive'a login oluyor bize izin vermek
için.Kullanıcı SkyDrive'a login oluyor bize izin vermek için.

Kullanıcı bizim yönlendirdiğimiz URL ile login olduktan sonra karşısına aşağıdaki gibi bir ekranda istediğimiz hakların listesi ve hangi uygulamanın istediği bilgisi gösteriliyor. Kullanıcı bu haklara onay verdiği gibi artık top bizde olacak.

Kullanıcıya vereceği haklar
soruluyoru.Kullanıcıya vereceği haklar soruluyoru.

Haklar da gördüğünüz üzere birincisi "Access your info anytime" hakkı var :) Yani istediğimiz zaman bu skydrive'a ulaşabilmek istiyoruz. İkincisi ve üçüncüsü ise direk SkyDrive ile ilgili. SkyDrive'daki fotoğraflardan tutun tüm dosyalara kadar ulaşabilir ve üçüncü hakka göre de düzenleme yapabiliriz.

Kullanıcı bu ekranda "Yes" dedikten sonra artık SkyDrive sitesinin bize haber vermesi gerek. Biz herhangi bir redirection tanımlamadık. O nedenle doğrudan https://oauth.live.com/desktop?code=XXXX diye bir sayfaya yönlendirme yapılacak. Bu sayfaya yapılan yönlendirmenincode parametresi ile gidip REST API'lerden erişim anahtarı (access token) isteyeceğiz.

Aşağıdaki minik kod ile WebBrowser componentinin Navigated eventini dinliyor ve eğer bizim istediğimiz sayfaya bir yönlendirme varsa QueryString üzerinden Code bilgisini alıp "access token" isteme işlemini başlatıyoruz.

[VB]

    Private Sub Browser_Navigated(sender As Object, e As NavigationEventArgs)\                                                                  Handles Browser.Navigated\         If e.Uri.ToString.StartsWith("https://oauth.live.com/desktop?code"Then\             Dim Code = System.Web.HttpUtility.ParseQueryString(e.Uri.Query).Get("code")\             Web.DownloadStringAsync(New Uri(\                  String.Format("https://oauth.live.com/token?" &\                                "client_id={0}&redirect_uri=" &\                                "https://oauth.live.com/desktop&code={1}&" &\                                "grant_type=authorization_code",\                                ClientID, Code)), "gettoken")\         End If\     End Sub

Access Token istemek için oauth.live.com'a bir GET atmamız gerekiyor :) Yukarıda Web adında gördüğünüz değişken basir bir WebClient nesnesi. Bu GET ile beraber de ClientID'mizi, redirecturi olarak desktop uygulamalarının default kullanması gereken desktop URL'ini ve code kısmına da biraz önce aldığımız Code bilgisini veriyoruz. Grant_Type olarak bir authorization_code istediğimizi de belirtip talebi başlatıyoruz. Ben ek olarak Context olsun diye "gettoken" contexti yolladım ama itiraf etmek gerekirse gereksiz oldu :) Aynı WebClient'ı kullanarak birkaç talep göndermek gerekir diye düşündüm ama gerekmedi :) Neyse, belki size lazım olursa Context üzerinden ayrıştırma şansınız olur.

[VB]

    Private Sub Web_DownloadStringCompleted(sender As Object, \                 e As DownloadStringCompletedEventArgsHandles Web.DownloadStringCompleted\         If e.UserState = "gettoken" Or e.UserState = "refreshtoken" Then\             Dim jss = New System.Web.Script.Serialization.JavaScriptSerializer\             Dim found = jss.Deserialize(Of Dictionary(Of StringString))(e.Result)\             refresh_token = found("refresh_token")\             access_token = found("access_token")\             upload_location = (jss.Deserialize(Of Dictionary(Of StringObject)) _\                                (Web.DownloadString(String.Format( _\                   "{0}/me/skydrive?access_token={1}", APIBase, access_token)))) _\                     ("upload_location")\             WpfApplication1.MySettings.Default.access_token = access_token\             WpfApplication1.MySettings.Default.upload_location = upload_location\             WpfApplication1.MySettings.Default.refresh_token = refresh_token\             WpfApplication1.MySettings.Default.Save()\ \             btn.IsEnabled = True\         End If\     End Sub

Yukarıdaki kodu okurken bir UserState kontrolü yaptığımı görüyorsunuz :) Aslında gerek yok. Refreshtoken olayına biraz gireceğiz ama State kontrolü yapmasam da olurmuş. Bir önceki adımda yaptığımız GET ile bize bir JSON dönüyor. Bu JSON içerisinde access_token (erişim anahtarı) ve refresh_token (yenileme anahtarı) geliyor. Access Token bizim herhangi bir yere erişebilmek için ihtiyacımız olan anahtarın ta kendisi fakat Access Token 3600 saniyede expire ediyor. Eğer yeni bir Access Token gerekirse Refresh Token ile tekrar bir talep gönderip yenisini alabiliyoruz. Unutmayın Refresh Token sadece Offline Erişim istediğinizde sağlanır. (Ki biz de bunu yapmıştık. JSON'un deserialize ettikten sonra tokenları ben hem değişkenlere hem de sonrasında WPF uygulamamda ayrıca ayarladığım kullanıcı ayarlarına atıyorum. Buradaki amaç aslında şu; WPF'in kullanıcı ayarlarına atıp veya belki de veritabanına atıp Access_Token ile Refresh_Token'ı saklarsanız uygulamanız kapanlıp açılsa aradan yıllar geçse bile :) tekrar bu bilgiler ile kullanıcının SkyDrive'ına istediğiniz zaman ulaşabilirsiniz.

Bir Upload yapalım!

Benim tüm bu işlere girmemin nedeni upload yapmaktı :) O nedenle sizinle upload örneğini paylaşacağım. Fakat upload dışında bu uygulamamız SkyDrive'da artık her tür hareketi yapabilir, klasör yaratmak, silmek vs herşey dahil. Detaylar içinLive SDK'ye buradan ulaşabilirsiniz. Bu arada unutmadan şu an için kullandığımız bu API'ler Beta! Ona göre hareket etmekte fayda var :)

Tam da Beta konusuna değinmişken biraz önce incelediğimiz kodun içindeki garip "upload_location" konusuna değinelim. Aslında gelin o satırı tekrar bir sahneye alalım :) çünkü pek insan oğlunun okuyabileceği şekilde yazmamışım!

[VB]

            upload_location = (jss.Deserialize(Of Dictionary(Of StringObject)) _\                                (Web.DownloadString(String.Format( _\                   "{0}/me/skydrive?access_token={1}", APIBase, access_token)))) _\                     ("upload_location")

Şimdi birincisi belli ki benim bir yerlerde tanımlanmış upload_location diye bir değişkenim var ve bunu bir şekilde WebClient üzerinden DownloadString ile bir GET yollayıp aldığım sonuca eşitliyorum. Hatta arada JSON Deserialize bile var tek satırda oy oy oy :) Demiştim insan okuyamaz diye :D Neyse konunun özeti şu; access_token ve refresh_token'ı aldıktan sonra ben hemen bir GET daha yollayıp SkyDrive'daki Root klasörün upload path'ini alıyorum. Bunun için bu oauth.live.com'a değil de APIBase diye değişkendeki adrese talep gönderdiğimi umarım farkına varabilmişsinizdir :) String.Format'taki ilk parametre :)

[VB]

    Dim APIBase As String = "https://beta.apis.live.net/v5.0" 'https://apis.live.net/v5.0

APIBase'i yukarıdaki şekilde "commenti dahil" tanımlamanızda fayda var :) Bunun nedeni daha önce de bahsettiğim gibi Beta API'leri kullanacak olmamız. Şu ana kadar yaptığımız herşey Live API'lerinin canlıda olan API'leriydi. Şu an itibari ile Beta API'leri çağıracağız. Bu API'ler yayına geçtiğinde tek yapmanız gereken commentli kısımdaki adresi kullanmak, o kadar!

VB]

            upload_location = (jss.Deserialize(Of Dictionary(Of StringObject)) _\                                (Web.DownloadString(String.Format( _\                   "{0}/me/skydrive?access_token={1}", APIBase, access_token)))) _\                     ("upload_location")

Bizim şu yukarıdaki GET'e geri dönersek :) /me/skydrive diyerek skydrive'ın root klasörü ile ilgili bilgi istemiş oluyoruz. Tabi bu arada access_token'ı da yolluyoruz. Gelen bilginin içerisindeki upload_location bu klasöre upload yapmak istediğimiz PUT talebimizi göndereceğimiz yer. 

[VB]

    Sub Upload(FilePath As String)\         Dim filename As String = System.IO.Path.GetFileName(FilePath)\         MessageBox.Show("Başlıyor!!!")\         Web.UploadData(String.Format("{2}/{0}?access_token={1}",\                                      filename, access_token,\                                      upload_location), "PUT",\                                  ReadFile(FilePath))\         MessageBox.Show("Bitti")\     End Sub

Şimdi artık Upload işlemine geçebiliriz. Elimide Upload Location var, access_token var. Yukarıdaki metodu sistemdeki bir dosyanın Path'ini verdiğim gibi upload_location'ının sonuna dosya adını ve parametre olarak da access_token'ı ekleyip PUT işlemini başlatıyor. MessageBox'ları siz silin bence :) Debug.WriteLine'dan daha çok seviyorum ben onları çıkardıkları ses nedeniyle :P

Son olarak dosyayı byte arraye çeviren ReadFile metodumu da paylaşiyim.

[VB]

    Public Shared Function ReadFile(filePath As StringAs Byte()\         Dim buffer As Byte()\         Dim fileStream As New FileStream(filePath, FileMode.Open, FileAccess.Read)\         Try\             Dim length As Integer = CInt(fileStream.Length)\             buffer = New Byte(length - 1) {}\             Dim count As Integer\             Dim sum As Integer = 0\             While (count = fileStream.Read(buffer, sum, length - sum)) > 0\                 sum += count\             End While\         Finally\             fileStream.Close()\         End Try\         Return buffer\     End Function

Peki ya RefreshToken sonradan nasıl kullanılıyor?

Uygulamanızın bir önceki açılışında bir access_token ve refresh_token aldığını (kullanıcının logini sonrasında) ve tekrar açıldığında da bunlara devam etmesi gerektiğini düşünürsek aslında tek yapmamız gereken hemen eldeki accees_token ile refresh_token'ı Live API'lere gönderip yenisini istemek.

[VB]

  Web.DownloadStringAsync(New Uri(\             String.Format("https://oauth.live.com/token?client_id={0}" &\                           "&redirect_uri=https://oauth.live.com/desktop" &\                           "&client_secret={1}&refresh_token={2}&" &\                           "grant_type=refresh_token", ClientID,\                           ClientSecret, refresh_token)),\         "refreshtoken")

Yukarıdaki GET talebimiz eldeki ClientID, ClientSecret ve RefresthToken'ı göndererek yeni bir access_token ve refresh_token istiyor. Özellikle grant_type'daki değişikliğe dikkat.

[VB]

    Private Sub Web_DownloadStringCompleted(sender As Object, \                 e As DownloadStringCompletedEventArgsHandles Web.DownloadStringCompleted\         If e.UserState = "gettoken" Or e.UserState = "refreshtoken" Then\             Dim jss = New System.Web.Script.Serialization.JavaScriptSerializer\             Dim found = jss.Deserialize(Of Dictionary(Of StringString))(e.Result)\             refresh_token = found("refresh_token")\             access_token = found("access_token")\             upload_location = (jss.Deserialize(Of Dictionary(Of StringObject)) _\                                (Web.DownloadString(String.Format( _\                   "{0}/me/skydrive?access_token={1}", APIBase, access_token)))) _\                     ("upload_location")\ \             WpfApplication1.MySettings.Default.access_token = access_token\             WpfApplication1.MySettings.Default.upload_location = upload_location\             WpfApplication1.MySettings.Default.refresh_token = refresh_token\             WpfApplication1.MySettings.Default.Save()\ \             btn.IsEnabled = True\         End If\     End Sub

RefreshToken ile gelen bir talebin sonucu ilk token talebinin sonucu ile aynı. Yani geri gelen JSON aynı :) O nedenle ben direk aynı Completed eventini bıraktım gitti :) İşte tam da bu yüzden UserState gereksiz oldu :)

İşlem Tamam

Dokümantasyonu şu an için muhteşem güzel olmasa da API'ler epey stabiller Beta olmalarına rağmen. O nedenle rahatlıkla development yapılabilir. SkyDrive'ın özellikle önümüzdeki dönemde WP7.5 ve Windows 8 ile beraber epey entegrasyonla daha da değer kazanacağını düşünürsek entegrasyon manzaralarını düşünmek anlamlı olabilir. Unutmadan :) Upload işleminde dosya formatlarında sınırlar var. Örneğin ZIP gönderemiyorsunuz ama ZIP'i JPG yapınca gönderiyor :) Benim için çok dert olmadı açıkçası. Diğer yandan göndereceğiniz dosyalar büyükse WebClient'ın UploadDataAsync asenkron metodunu da kullanabilirsiniz.

Umarım işinize yarar! Hepinize kolay gelsin ;)

Bu makale Live Connect SDK SkyDrive Developer Preview / BetaREST API'leri kullanılarak hazırlanmıştır.