讓Google Drive能夠支援匿名上傳檔案(未登入即可上傳)

在雲端檔案中心百家爭鳴的年代,迄今能夠讓一般使用者隨手設定即可支援匿名上傳(Anoymous Upload)的,也只有DropBox一家。原因無它,匿名上傳意味著一個網路的匿名(未經過任何身分驗證)使用者,就可以透過介面去上傳到你自己所擁有的儲存空間,這對一間以商業導向的軟體公司來說,根本沒有任何的甜頭可言,所以在2016年的此刻,我們仍可以發現大如Google、Microsoft等雲端軟體服務(Google Drive、Microsoft OneDrive)等,仍然不願意提供匿名上傳的服務。

但路是人走出來的,正所謂山不轉路轉,有人發現利用Google開放給自己旗下辦公室軟體的操作語言:Google Apps Script,擁有讀寫Google Drive的能力,這意味著可以透過Google Apps Script來達成使用者與自己後端擁有的Google Drive進行溝通的橋樑,這也就是說,匿名上傳檔案到Google Drive再也不是不可能的事了。

讓Google Drive支援匿名上傳檔案全攻略

Step 1. 登入你的Google帳號後,先到Google Apps Script的首頁:https://www.google.com/script/start/,點選畫面中間的「Start Scripting」按鈕,開始一個全前後端JavaScript語言的創作空間。

Step 2. 打開畫面後,你可以先到右上角,點選「無標題專案」,幫你這個Script重新命名。重新命名後,基本上你會在你的Google Drive中,看到以這個名字為命名的Google Apps Script檔案。

Step 3. 由於我們是要透過網頁跟「匿名」的一般使用者溝通,因此有一個網頁介面是必要的,在下圖我們示範了如何去新增一個HTML檔案資料,當然很沒創意的,我們就叫他「MyForm.html」。

Step 4. 點選左側的「MyForm.html」,並填入下列的HTML。先聲明,在這篇網誌中,我並不打算講解任何的前後端HTML、JS程式碼的意思,我已經盡量在段落的字裡行間加了一些註解,這種小孩子等級的程式碼應該難不倒各位Coding大師的啦!

<!DOCTYPE html>
<html>

<head>
	<base target="_top">
    <script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
</head>

<body style="margin: 2em">

    <div class="row" style="margin-bottom:1em">
        <div class="col-md-12">
            <h1><span class="glyphicon glyphicon-grain" aria-hidden="true"></span> 請上傳PDF檔案<small> Upload PDF File</small></h1>
        </div>
    </div>
    
    <div class="row">
        <div class="col-md-6">
           <div class="panel panel-primary">
              <div class="panel-heading">
                  <h3 class="panel-title">請依照下方規定上傳你的檔案 Please upload your file by following requirements</h3>
              </div>
              <div class="panel-body">
                  <p>1. 檔案名稱:OOOOO</p>
                  <p>2. 檔案格式:PDF格式(File type: PDF Format)</p>
                  <p>3. 檔案大小:最大限定 2M Bytes(File size: 2M Bytes limited)</p>
              </div>
           </div>
        </div>
    </div>
    
    <div class="row">
        <div class="col-md-6">
            <form id="uploadForm">
                <div class="form-group">
                    <label for="applicationFile"><span class="glyphicon glyphicon-download" aria-hidden="true"></span> 請於下方點選瀏覽按鈕,並上傳您的PDF檔案。</label>
                    <p class="help-block">Click the "Browse" button below, and upload your PDF File.</p>
                    <input type="file" id="applicationFile" name="applicationFile">
                </div>
                <button type="button" id="btnUpload" class="btn btn-success">
                    <span class="glyphicon glyphicon-ok" aria-hidden="true"></span> 
                    Upload PDF file
                </button>
            </form>
        </div>
    </div>
    
    <div class="row" style="margin-top:1em">
        <div class="col-md-6">
            <div id="cMsg" role="alert"></div>
        </div>
    </div>
    
    <script>
	$(document).ready(function(){
		$("#btnUpload").click(function(){
            $(this).prop("disabled", true).addClass("disabled");
			$("#cMsg").html("<div class='progress'><div class='progress-bar progress-bar-success progress-bar-striped active' role='progressbar' aria-valuenow='100' aria-valuemin='0' aria-valuemax='100' style='width: 100%'><span class='sr-only'>Uploading...</span></div></div>");
			google.script.run.withSuccessHandler(function(e){
                if (e.indexOf('failed') == -1)
                { //上傳成功
                    $("#cMsg").html("<div class='alert alert-success' role='alert'><span class='glyphicon glyphicon-info-sign' aria-hidden='true'></span><strong> System Message: </strong>" + e + "</div>");
                }
                else
                { //上傳失敗
                    $("#btnUpload").prop("disabled", false).removeClass("disabled");
                    $("#cMsg").html("<div class='alert alert-danger' role='alert'><span class='glyphicon glyphicon-info-sign' aria-hidden='true'></span><strong> System Message: </strong>" + e + "</div>");
                }
            }).uploadFiles(this.parentNode);
			return false;
		});
	});
    </script>
    
</body>
</html>

Step 5. 點選左側的「程式碼.gs」(.gs就是Google Script的意思),把所有的程式碼取代成下列程式碼。同樣的,這種幼兒等級的程式碼,應該難不倒各位程式設計師之神的對吧!所以我就不再說明了。

function doGet(e) {
  return HtmlService.createHtmlOutputFromFile('MyForm.html');
}

function uploadFiles(form) {
  try
  { 
    var parentFolderName = "匿名上傳資料夾";
    var parentFolders = DriveApp.getFoldersByName(parentFolderName);
    var parentFolder;
    
    //由於可能具有眾多同名稱的資料夾,因此在這邊做預設處理
    if (parentFolders.hasNext())
    { parentFolder = parentFolders.next(); }
    else //如果資料夾不存在就自動建立
    { parentFolder = DriveApp.createFolder(parentFolderName); }
    
    var blob1 = form.applicationFile;
    var errMsg = "";
    
    //檢查檔案正確性
    if (blob1.length <= 0)
    {
      errMsg = "Application form file size can not be 0 Byte.";
    } else if (blob1.getName().toUpperCase().indexOf(".PDF") == -1)
    {
      errMsg = "Application form file must be uploaded in PDF format.";
    }
    
    //如果不正確就踢回去
    if (errMsg != "")
    {
      return "Upload failed! Reason: " + errMsg;
    }
    
    //處理檔案寫入工作
    if (blob1.length > 0) {
      //自動以年分新增資料夾
      var subFolderName = new Date().getFullYear();
      var subFolders = parentFolder.getFoldersByName(subFolderName);
      var subFolder;
      
      //由於可能具有眾多同名稱的資料夾,因此在這邊做預設處理
      if (subFolders.hasNext())
      { subFolder = subFolders.next(); }
      else //如果資料夾不存在就自動建立子資料夾
      { subFolder = parentFolder.createFolder(subFolderName); }
      
      //寫入資料
      var file1 = subFolder.createFile(blob1);
    }
    return "Upload successfully!";
  }
  catch (error)
  { 
    return "Upload failed! Reason: " + error.toString();
  }
}

Step 6. 取得這支Google Apps Script對你的OneDrive之讀寫權限!點選「執行」>「doGet」,跑一下doGet裡面的程式碼。

Step 7. 彈出「需要授權」視窗,請核對授權並允許吧!

Step 8. 發佈你的程式碼,請點選「發佈」>「部署為網路應用程式」。

Step 9. 專案版本請每次都挑選「新增」,否則將會測不出你最新修改的前後端程式碼。另外有一點超級重要的,就是紅框中的「具有應用程式存取權的使用者」,這個一定要選「任何人,甚至是匿名使用者」,否則你無法真正的匿名檔案上傳。一切都確定後,點選部屬按鈕。

Step 10. 最後,將畫面中的應用程式網址,COPY給你要應用的對象(一般使用者),他們就可以透過這個程式碼將檔案直接免登入,送到你的Google Drive之中了。

後記與心得

  1. 程式碼中的parentFolders.hasNext();等檢查機制,對於嚴重缺乏類別庫的JavaScript來說,頗具有無奈中之巧妙,值得玩味。當然啦,對於C#之於.Net Framework來說,這種機制就變成是笑話了。
  2. 對於前端的form表單來說,text或其他field的input properties,請注意加上「name」屬性。對於前後端都早已習慣使用ID屬性的你來說,我只能請你回想一下HTTP的POST機制,你會有會心一笑的感覺。
  3. 真心地覺得在後端用JS寫Code,純粹是一種自虐的行為(有在寫後端語言的就知道我在講什麼)。論開發效率或速度,都屬於下乘。不過這時代就是這樣,大家一窩蜂往這種語言跑,所有的資源都往這邊挹注,最終的結果就是這裡資源超級豐富(一堆套件、框架等),你不跳進來,有時候反而又變成你自己在自虐了。
  4. 千萬不要濫用匿名上傳這個功能,你要知道,你的Google Drive應該都是免費版本的,江湖上傳言上傳超過1G後,就變成不能上傳了。(沒驗證過)
GoogleDrive GoogleAppsScript AnonymousFileUpload