"Enter"a basıp içeriğe geçin

.NET ile Google Drive API Kullanma

Google Drive .net kütüphanesi için verilen örneklerin neredeyse hepsi GoogleAuthorizationWebBroker kullanılarak yapılmış. Yani authentication işlemi için bir tarayıcı açılarak kullanıcıdan izin isteme ekranı karşımıza çıkıyor. Ben .net core ile yapmış olduğum bir uygulamada, sunucuya yüklenen dosyaların aynı zamanda Google Drive hesabıma da yüklenmesini istiyordum. Tarayıcı ile yetkilendirme işlemi uygun olmadığı için Google API dökümanlarını araştırdım ve böyle bir durumda “Service Account” kullanmam gerektiğini anladım. Böyle bir senaryo, bir web uygulaması ile Google servisi arasındaki etkileşim, “server-to-server” olarak adlandırılmış.

İlk olarak son kullanıcıya değil de uygulamaya ait olan bir “Service Account” oluşturmamız gerekiyor.

https://developers.google.com/identity/protocols/oauth2/service-account

Burada detaylı olarak “Service Account” oluşturma aşamaları anlatılmış ben kısaca bahsedeceğim.

  • Service Accounts Sayfasını Açın
  • Eğer daha önceden bir proje oluşturduysanız seçin veya yeni bir proje oluşuturun
  • Projenizi seçtikten sonra “Create Service Account” bağlantısına tıklayın.
  • “Service Account Name” ve “Description” kısımlarını doldurun. Ardından Create butonuna tıklayın.
  • “Service account permissions” kısmında “IAM” altında bulunan iki rolü ekleyin
  • Son kısımda ise kullanacağınız Google Drive hesabınızın adresini girin. (Burası gerekli mi emin değilim.)
  • Done butonuna basarak hesabı oluşturun.

Hesabımız oluştu, kimlik bilgilerini almak için “Service Accounts” sayfasında “Actions” sekmesinin altında bulunan üç noktaya basıp “Create Key” butonuna basın. Kimlik bilgilerinin olduğu bir dosyanın indirilmesi gerekiyor. Bu dosyayı birazdan kullanacağız.

API kullanmak için boş bir solution oluşturuyorum. Ardından “GoogleDriveAPI” adında bir .net standart projesi oluşturuyorum. Az önce indirmiş olduğum json dosyasını bu projenin ana dizinine atıyorum ve properties kısmından “copy to output seçeneğini “always copy” olarak değiştiriyorum. Nuget üzerinden Google.Apis.Drive.v3 paketini yüklüyorum.
IGoogleDriveAPI adında bir interface oluşturuyorum.

       
    public interface IGoogleDriveAPI
    {
        Task< UploadResult > UploadFile(string path, bool setPublic);
        Task< string > MakeDriveFilePublic(DriveService service, string fileId);
    }

Dosya yüklendikten sonra döndürülecek UploadResult

       
    public class UploadResult
    {
        public string Id { get; set; }
        public string WebContentLink { get; set; }
    }

Şimdi sıra IGoogleDriveAPI interface’ni uygulamakta. Ana dizinde GoogleDriveAPI class’ını oluşturuyorum.

İki adet field oluşturuyorum.

        private readonly string[] Scopes = { DriveService.Scope.Drive };
        private readonly string ApplicationName = "Uygulama İsmi";

Scope ile uygulamanın yapabileceklerini belirtiyoruz. “DriveService.Scope.Drive” scope’u tüm Google Drive dosyalarını görme, düzenleme, silme ve yeni dosyalar oluşturma yetkilerine sahip.

İndirmiş olduğumuz kimlik bilgileri ile bir “DriveService” oluşturup döndüren private bir method oluşturuyorum. Dosya işlemlerimizi oluşturulan “DriveService” üzerinden gerçekleştireceğiz.

       
        private DriveService GetDriveServiceInstance()
        {
            GoogleCredential credential;
            using (var stream = new FileStream("quickstart-1561542937896-3860ed38tra3.json", FileMode.Open, FileAccess.Read))
            {
                credential = GoogleCredential.FromStream(stream).CreateScoped(Scopes);
            }
            var service = new DriveService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = ApplicationName
            });
            return service;
        }

FileStream path kısmına projenin ana dizinine atmış olduğumuz json dosyasının adını yazın.

Dosya işlemlerine geçmeden önce belirtmem gereken önemli bir şey var. “Service Account” ile yaptığınız işlemleri Google Drive hesabınızdan göremiyorsunuz. Yani bir dosya yükleme işlemi yaptığınızda drive üzerinde bu dosyayı göremeyeceksiniz. Burada yapmamız gereken şey Google Drive hesabımızda bir klasör oluşturup bunu service account ile paylaştırmak. Bunun için https://console.developers.google.com/iam-admin/serviceaccounts adresine gidip projenizi seçin. Açılan sayfada “gserviceaccount.com” ile biten mail adresini kopyalayın. Drive hesabınızda bir klasör oluşturun, ben serviceaccount adını verdim. Oluşturulan klasöre sağ tıklayın ve paylaş butonuna basın. Az önce kopyaladığınız mail adresini girin ve paylaşın. Paylaştığınız klasörü açın ve adres çubuğunda 1B7ro-YCCW… kısmını kopyalayın, dosya yüklerken bu koda ihtiyacımız olacak.

Tekrar kod kısmına dönelim ve dosya yükleme methodumuzu implement edelim.

public async Task< UploadResult > UploadFile(string path)
{
    var service = GetDriveServiceInstance();
    var uploadResult = new UploadResult();

    var mimeType= Path.GetExtension(path).GetMimeTypeFromExtension();
    var fileMetadata = new Google.Apis.Drive.v3.Data.File();
    fileMetadata.Name = Path.GetFileName(path);
    fileMetadata.MimeType = mimeType;
    fileMetadata.Parents = new List() { "az önce kopyalamış olduğunuz klasörün id'sini buraya yapıştırın"};
    FilesResource.CreateMediaUpload request;
    using (var stream = new FileStream(path, FileMode.Open))
    {
        request = service.Files.Create(fileMetadata, stream, mimeType);
        request.Fields = "id"; //Geri dönmesini istediğiniz alanları fields kısmında belirtmeniz gerekiyor.
        await request.UploadAsync();
    }

    var file = request.ResponseBody;
    uploadResult.Id = file.Id;

    return uploadResult;
}

Dosya yüklerken parametrelerde MimeType belirtmemiz gerekiyor. GetMimeTypeFromExtension dosya uzantısından MimeType döndüren bir extension method. Örneğin .png uzantısı için “image/png” gibi.
Uzun olduğu için gist olarak paylaşıyorum. https://gist.github.com/ucanbaklava/ba01bd2a8a885452405a812293c2ab3b

Dosyayı public olarak paylaşan ve linkini geri döndüren method


public async Task< string > MakeDriveFilePublic(DriveService service, string fileId)
{
    var publicPermission = new Permission();
    publicPermission.Type = "anyone";
    publicPermission.Role = "reader";

    await service.Permissions.Create(publicPermission, fileId).ExecuteAsync();
    var req = service.Files.Get(fileId);
    //req.fields = "webviewlink"
    req.Fields = "webContentLink";
    var response = req.Execute();
    return response.WebContentLink;
}

Dosyanın herkese açık olarak paylaşılabilmesi için “type” izni “anyone”, “role” izni ise “reader” olarak belirtilmeli. Fields kısmında belirttiğimiz “webContentLink” ise dosyanın direkt indirme adresidir (hotlink). “webViewLink” ise dosyayı Google Drive üzerinde gösteren adrestir.

GoogleDriveAPI.cs son hali

public class GoogleDriveAPI : IGoogleDriveAPI
{
    private readonly string[] Scopes = { DriveService.Scope.Drive };
    private readonly string ApplicationName = "ucanbaklava";

    public async Task< string > MakeDriveFilePublic(DriveService service, string fileId)
    {
        var publicPermission = new Permission();
        publicPermission.Type = "anyone";
        publicPermission.Role = "reader";

        await service.Permissions.Create(publicPermission, fileId).ExecuteAsync();
        var req = service.Files.Get(fileId);
        //req.fields = "webviewlink"
        req.Fields = "webContentLink";
        var response = req.Execute();
        return response.WebContentLink;
    }

    public async Task< UploadResult > UploadFile(string path, bool setPublic)
    {
        var service = GetDriveServiceInstance();
        var uploadResult = new UploadResult();

        var mimeType = Path.GetExtension(path).GetMimeTypeFromExtension();
        var fileMetadata = new Google.Apis.Drive.v3.Data.File();
        fileMetadata.Name = Path.GetFileName(path);
        fileMetadata.MimeType = mimeType;
        fileMetadata.Parents = new List() { "1B7to-YCCGzQl52vPJzi5yldc4FyNyBC5"};
        FilesResource.CreateMediaUpload request;
        using (var stream = new FileStream(path, FileMode.Open))
        {
            request = service.Files.Create(fileMetadata, stream, mimeType);
            request.Fields = "id";
            await request.UploadAsync();
        }

        var file = request.ResponseBody;
        uploadResult.Id = file.Id;
        if(setPublic)
            uploadResult.WebContentLink = await MakeDriveFilePublic(service, file.Id);
        return uploadResult;
    }

        
    private DriveService GetDriveServiceInstance()
    {
        GoogleCredential credential;
        using (var stream = new FileStream("quickstart-1561542937896-3860ed38tra3.json", FileMode.Open, FileAccess.Read))
        {
            credential = GoogleCredential.FromStream(stream).CreateScoped(Scopes);
        }

        var service = new DriveService(new BaseClientService.Initializer()
        {
            HttpClientInitializer = credential,
            ApplicationName = ApplicationName
        });

        return service;
    }
}

Test etmek için bir xUnit projesi oluşturuyorum. Basit bir test yazıp çalıştırıyorum.

public class GoogleDriveAPITests
{
    [Fact]
    public void Test()
    {
        var api = new GoogleDriveAPI();
        var result = api.UploadFile("bayrak.jpg", true);
        Assert.StartsWith("http", result.Result.WebContentLink);
    }
}

Test başarılı. Kontrol etmek için google drive üzerinde paylaştırdığım klasörü kontrol ediyorum.

2 Yorum

  1. foo
    foo Ekim 19, 2020

    Merhaba hocam,

    Bende aynı konuyu Google Sheets Api için kullanmak istiyorum.
    Denedim ve implemantasyonunu sağladım.

    Aklıma takılan, “Service Account” ücretli midir?
    Teşekkürler.

    • admin
      admin Ekim 20, 2020

      ben kart bilgisi eklemeden service account kullanıyorum, ücretli olduğunu düşünmüyorum.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir