2015年4月7日 星期二

Google Drive API

Google Drive 的授權方式

要透過 Google API 存取 Google Drive ,不管存取的檔案權限設定為何,你的 App 都必須先獲得使用者的「開放授權」才可以讀取。 目前 Google Drive API 支援 OAuth 2.0 協定。

Google Drive 的物件類別

在 Google Drive 中,不管是目錄或者檔案,都是使用 File (命名空間:Google.Apis.Drive.v2.Data)類別表示,所以針對檔案或目錄進行查詢、刪除、修改、權限設定等操作,都是使用相同的方式,所以底下的範例就不重覆說明。

類別庫下載

透過 NuGet 下載以下類別庫套件:

  • Google APIs Client Library
  • Google APIs Auth Client Library
  • Google.API.Drive.V2 Client Library

存取 Google Drive

如何取得檔案清單

檔案與目錄的差別

在 Google Drive 檔案架構中,不管目錄或檔案都使用 File 物件表示。 唯一的差別是,若是目錄,它的 MimeType 屬性值必定等於 "application/vnd.google-apps.folder"。

父與子

在 File 的屬性中,你可以透過 Parents 屬性,取得其父層節點物件(ParentReference)。 如果檔案位於根節點下之下,那麼它的父層結點中的 IsRoot 屬性會等於 true 。

搜尋

要搜尋檔案,跟取得檔案清單都是使用相同的方法,差別在於搜尋條件的設定。 當執行 List() 方法時,若沒有設定任何條件,那麼回傳的清單也會包含垃圾筒中的檔案。 若要過濾這些內容,可以在 ListRequest 物件中設定 Q 屬性,用以加入篩選條件。 相關的篩選條件設定,可參考 https://developers.google.com/drive/web/search-parameters

  • List():這個方法可以取得使用者的檔案清單。
/// <summary>
    /// 取得檔案清單
    /// Documentation List: https://developers.google.com/drive/v2/reference/files/list 
    /// Documentation Search: https://developers.google.com/drive/web/search-parameters 
    /// </summary>
    /// <param name="searchPattern">搜尋條件</param>
    /// <returns></returns>
    public List<GData.File> List(string searchPattern = "*")
    {
        List<GData.File> result = new List<GData.File>();
        try
        {
            FilesResource.ListRequest request = service.Files.List();
            request.MaxResults = 1000;
            if (searchPattern != "*")
            {
                request.Q = searchPattern;
            }
            GData.FileList filesFeed = request.Execute();

            // 判斷資料是否回傳結束
            while (filesFeed.Items != null)
            {
                // add to the list
                result.AddRange(filesFeed.Items);
                    
                if (filesFeed.NextPageToken != null)
                {
                    // 若有下一頁,繼續
                    request.PageToken = filesFeed.NextPageToken;

                    // 執行 NextPage 的 request
                    filesFeed = request.Execute();
                }
                else
                {
                    // 若沒有下一頁,結束
                    break;
                }
            }
        }
        catch (Exception ex)
        {
            throw;
        }
        return result;
    }

底下示範幾個篩選條件

public List<GData.File> GetList(string folderid = "")
    {
        string searchPattern = "trashed=false";
        searchPattern += string.Format(" and '{0}' in owners", owner);

        if (folderid != "")
            searchPattern += string.Format(" and '{0}' in parents", folderid);
        List<GData.File> result = List(searchPattern);
        return result;
    }
public List<GData.File> GetFolderList(string folderid = "")
    {
        string searchPattern = "trashed=false";
        searchPattern += " and mimeType='application/vnd.google-apps.folder'";
        searchPattern += string.Format(" and '{0}' in owners", owner);

        if (folderid!="")
            searchPattern += string.Format(" and '{0}' in parents", folderid);
        List<GData.File> result = List(searchPattern);
        return result;
    }
//找出近一個月有修改過的檔案
    DateTime date = DateTime.Now.AddMonths(-1);
    string date_utc = date.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss");

    string search = "trashed=false";
    search += string.Format(" and '{0}' in owners", gstrUserEmail);
    search += " and modifiedDate > '" + date_utc + "'";

    List<GData.File>  files= service.List(search);

如何建立目錄

若是目錄,其 MimeType 屬性值必定等於 "application/vnd.google-apps.folder",才可以當成其他檔案的父層成員。

/// <summary>
/// 建立一個 File 物件
/// </summary>
/// <param name="parentid">父層目錄ID</param>
/// <param name="title">File's Name</param>
/// <param name="description">File's Description</param>
/// <param name="mimeType">File's MimeType</param>
/// <returns></returns>
private GData.File GenFileObject(string parentid , string title, string description="", string mimeType = "")
{
    try
    {
        GData.File body = new GData.File();
        body.Title = title;
        body.Description = description;
        body.MimeType = mimeType;
        if (parentid != "")
            body.Parents = new List<GData.ParentReference>() { new GData.ParentReference() { Id = parentid } };

        return body;
    }
    catch (Exception ex)
    {
        throw;
    }
}

public GData.File CreateFolder(string parentid , string title, string description = "")
{
    try
    {
        // setting file
        string mimeType = "application/vnd.google-apps.folder";
        GData.File body = GenFileObject(parentid, title, description, mimeType);
        FilesResource.InsertRequest request = service.Files.Insert(body);
        GData.File folder = request.Execute();
        return folder;
    }
    catch
    {
        throw;
    }
}

如何上傳檔案

要上傳檔案到 Google Drive ,都必須指定一個 mime-type ,底下有個小函式,可以自動取得預設的 mime-type ,方便上傳資料時使用。 另外 Google Drive 同一個檔案上傳二次,即使檔案名稱都相同,它還是會認定為二個不同的檔案,擁有不同的檔案 ID. 如果要更新檔案內容,請看稍後的文章。

/// <summary>
/// 上傳檔案
/// </summary>
/// <param name="fileSource">local 端的檔案路徑</param>
/// <param name="parentid">要上傳到 google drive 上的 folder id</param>
/// <param name="mimeType">指定 mimetype,若沒指定由系統看動判斷</param>
/// <returns></returns>
public GData.File Upload(string fileSource, string parentid = "", string mimeType = "")
{
    FileInfo file = new FileInfo(fileSource);
    if (file.Exists)
    {
        // setting file
        if (mimeType=="")
            mimeType=(mimeType == "" ? GetMimeType(fileSource) : mimeType);
        GData.File body = GenFileObject(parentid, file.Name, file.Name, mimeType);
                
        // read file to stream
        byte[] byteArray = System.IO.File.ReadAllBytes(fileSource);
        System.IO.MemoryStream stream = new System.IO.MemoryStream(byteArray);

        try
        {
            FilesResource.InsertMediaUpload request = service.Files.Insert(body, stream, body.MimeType);
            request.Upload();
            return request.ResponseBody;
        }
        catch (Exception ex)
        {
            throw;
        }
    }
    else
    {
        throw (new Exception("檔案不存在,無法上傳。"));
    }
}

/// <summary>
/// 自動判斷檔案的 MimeType
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
private string GetMimeType(string fileName)
{
    string mimeType = "application/unknown";
    string ext = System.IO.Path.GetExtension(fileName).ToLower();
    Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);
    if (regKey != null && regKey.GetValue("Content Type") != null)
        mimeType = regKey.GetValue("Content Type").ToString();
    return mimeType;
}

如何下載檔案

Google Drive 中,有一些檔案是由 Google 的其他服務自動建立的,例如 Google Map 或 Doc 文件等等,所以並不是所有檔案都可以下載。 不過,只要是使用者上傳的檔案都沒有問題,你都可以在 File 物件中找到 DownloadUrl 屬性,這個值就是用來下載檔案使用的。

/// <summary>
    /// 下載檔案
    /// </summary>
    /// <param name="DownloadUrl">要下載的檔案的 url </param>
    /// <returns></returns>
    public byte[] Download(string DownloadUrl)
    {
        try
        {
            Task<byte[]> request = service.HttpClient.GetByteArrayAsync(DownloadUrl);
            byte[] result = request.Result;
            return result;
        }
        catch (Exception ex)
        {
            throw;
        }
    }

如何更新檔案

這裡的「更新」指的是重新上傳檔案內容,這樣的更新不會變動檔案 ID ,可以避免因為檔案異動而導至一些舊有的連結失效。

/// <summary>
/// 重新上傳檔案
/// </summary>
/// <param name="fileid"></param>
/// <param name="filepath"></param>
/// <param name="parentid"></param>
/// <param name="mimeType"></param>
/// <returns></returns>
public GData.File ReUpload(string fileid, string filepath, string parentid = "", string mimeType = "")
{
    FileInfo file = new FileInfo(filepath);
    if (file.Exists)
    {
        // setting file
        if (mimeType=="")
            mimeType=(mimeType == "" ? GetMimeType(filepath) : mimeType);
        GData.File body = GenFileObject(parentid, file.Name, file.Name, mimeType);

        // read file to stream
        byte[] byteArray = System.IO.File.ReadAllBytes(filepath);
        System.IO.MemoryStream stream = new System.IO.MemoryStream(byteArray);

        try
        {
            FilesResource.UpdateMediaUpload request = service.Files.Update(body, fileid, stream, body.MimeType);
            request.Upload();
            return request.ResponseBody;
        }
        catch (Exception ex)
        {
            throw;
        }
    }
    else
    {
        throw (new Exception("檔案不存在,無法上傳。"));
    }
}

如何修改、刪除檔案

下面幾個方法可以用來修改刪除檔案或目錄。

  • Patch:如果你只是想「修改」檔案的 MetaData ,可以使用這個方法。
  • Delete:刪除檔案。
  • Trash:將檔案移除到垃圾筒資料夾。
  • Untrash:將檔案由垃圾筒復原。
/// <summary>
/// 修改檔案或目錄的 title 和 description 欄位資訊,若不修改,可傳入 null 值。
/// </summary>
/// <param name="fileid"></param>
/// <param name="newTitle"></param>
/// <param name="newDescription"></param>
/// <returns></returns>
public GData.File Patch(string fileid, string newTitle = null, string newDescription = null)
{
    try
    {
        GData.File file = new GData.File();
        if (newTitle != null)
        {
            file.Title = newTitle;
        }
        if (newDescription != null)
        {
            file.Description = newDescription;
        }

        FilesResource.PatchRequest request = service.Files.Patch(file, fileid);
        return request.Execute();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        return null;
    }
}

/// <summary>
/// 刪除檔案或目錄
/// </summary>
/// <param name="fileid"></param>
/// <returns></returns>
public void Delete(string fileid)
{
    try
    {
        service.Files.Delete(fileid).Execute();
    }
    catch (Exception ex)
    {
        throw;
    }
}

/// <summary>
/// 將檔案或目錄移至垃圾筒
/// </summary>
/// <param name="fileid"></param>
/// <returns></returns>
public GData.File Trash(string fileid)
{
    try
    {
        return service.Files.Trash(fileid).Execute();
    }
    catch (Exception ex)
    {
        throw;
    }
}

/// <summary>
/// 將檔案或目錄由垃圾筒復原
/// </summary>
/// <param name="fileid"></param>
/// <returns></returns>
public GData.File Untrash(string fileid)
{
    try
    {
        return service.Files.Untrash(fileid).Execute();
    }
    catch (Exception ex)
    {
        throw;
    }
}

如何設定 Google Drive 的分享權限

在 Google Drive 中,不管檔案或是目錄,都是使用 ACL (Access Control List) 來管控分享設定,它可以針對不同類型(type)的人,設定不同的操作權限(role)。 例如:針對(anyone)給予(readder)權限;或者針對(特定使用者)給予(writer)權限。 詳細介紹可參考 Drive REST API : Share Files 文件。

底下示範幾個常用到的設定:

如何將檔案設定為「公開的」

public void SetPermission_Public(string fileId)
{
    GData.Permission permission = new GData.Permission();
    permission.Role = "reader";
    permission.Type = "anyone";
    permission.Value = "";
    driveService.Permissions.Insert(permission, fileId).Execute();
}

如何將檔案設定為「僅限知道連結的使用者」

原則上,若要將檔案設定為「僅限知道連結的使用者」,那麼在設定 permission 時,只要多設定一道屬性 permission.WithLink = true 即可。 不過,因為 Permissions 本身是一個 List 物件,也就是你可以一次設定多個權限值,如果你同時針對 anyone 給予 public 和 withlink ,那麼這個檔案還是會以 public 為主,因為它包含的範圍較大。 所以,下面範例中,我們先去除 anyone 的權限設定,再加入新的 permission 。

public void SetPermission_PublicWithLink(string fileId)
{
    GData.Permission permission = new GData.Permission();
    permission.Role = "reader";
    permission.Type = "anyone";
    permission.Value = "";
    permission.WithLink = true;

    RemovePermission(fileId, "anyone");
    driveService.Permissions.Insert(permission, fileId).Execute();
}
private string RemovePermission(string fileId, string permissionId)
{
    string result = "";
    try
    {
        // 刪除 Permissions.items 集合中,id = permissionId 的項目。
        result = driveService.Permissions.Delete(fileId, permissionId).Execute();
    }
    catch (Exception ex)
    {
        result = ex.Message;
    }
    return result;
}

如何將檔案設定為「私有的」

要將檔案設定為「私有的」,簡單來說,就是移除 anyone 和 anyoneLink 的權限設定。

public void SetPermission_Private(string fileId)
{
    PermissionsResource.ListRequest request = driveService.Permissions.List(fileId);
    GData.PermissionList list = request.Execute();

    RemovePermission(fileId, "anyone");
    RemovePermission(fileId, "anyoneWithLink");
}

如何直接在 browser 中瀏覽 Google Drive 中的檔案

在 Google.Apis.Drive.v2.Data 的 File 物件中,有幾個與連結有關的屬性,可用來取得該檔案的相關資訊,例如:

  • AlternateLink:A link for opening the file in a relevant Google editor or viewer.
  • WebContentLink:A link for downloading the content of the file in a browser using cookie based authentication. In cases where the content is shared publicly, the content can be downloaded without any credentials.
  • SelfLink:A link back to this file.

上述列表中,WebContentLink 就是一個可以讓你透過 browser ,直接下載該檔案用的連結。 不過,如果你想要利用 Google Drive 來當做圖床或是網頁主機的話,那麼你必須使用底下格式的連結,才可以直接在 browser 中檢視:

www.googledrive.com/host/{文件ID}

例如,你有底下二個檔案,其中 helloworld1.htm 的 ID 為 0BwEj7exTMd2DMDd4X1JaUlFqR28 ,helloworld2.htm 的 ID 為 0BwEj7exTMd2DX21fOG1uLU9DWVU 。

/Webs/W001/helloworld1.htm
/Webs/W002/helloworld2.htm

你就可以使用底下連結,直接在 browser 中檢視它們。

www.googledrive.com/host/0BwEj7exTMd2DMDd4X1JaUlFqR28
www.googledrive.com/host/0BwEj7exTMd2DX21fOG1uLU9DWVU

上述的文件ID,也可以是一個目錄 ID ,而且透過目錄 ID ,在該目錄下的子目錄或檔案的連結,都可以使用檔案的 Title 屬性直接串接取得。 例如,若 Webs 這個目錄的 ID 為 0BwEj7exTMd2DQ2wxRmdWSGpDV0U ,你就可以使用以下連結直接瀏灠:

www.googledrive.com/host/0BwEj7exTMd2DQ2wxRmdWSGpDV0U/W001/helloworld1.htm
www.googledrive.com/host/0BwEj7exTMd2DQ2wxRmdWSGpDV0U/W001/helloworld2.htm

也就是說,你只要知道最上層子目錄的ID(必須設定公開屬性),那麼在該子目錄下的所有檔案,你就不用理會他們的ID為何,都可以輕鬆的由 title 名稱取得瀏覽連結。 所以,你只要注意底下二點,就可以將 Google 雲端硬碟,當做是網頁空間來使用。

  • 目錄或檔案都要設定為公開屬性。
  • 不要使用中文檔名。

沒有留言:

張貼留言