【问题标题】:Cant upload to subfolder on Google Drive with Google App Script无法使用 Google App Script 上传到 Google Drive 上的子文件夹
【发布时间】:2020-05-26 16:03:49
【问题描述】:

我的代码有一个成功的工作版本,它允许用户选择一个文件并上传到我的 Google 云端硬盘。 我正在使用一种将文件分块上传为可恢复上传的方法,以绕过 50mb 的限制。它很棒!但它只会上传到我的 Google Drive 的根文件夹!

我一直在尝试实施this 解决方案,但接受的答案只是一个猜测(由回答的人承认),原始发帖人的评论说他们为使其工作所做的工作对我也不起作用.

我目前有它用于初始化要上传的文件:

xhr.open("POST", "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&addParents=1XVNmZ7Q4vHLB7p9tTk0_rz7Z1VOwy_I8&removeParents=root");

这不会产生错误,但只是将文件放在根目录中。

我也尝试更改“恢复”的 PUT 请求,但如果我与原始 POST 不匹配或者如果我确实使它们相同,它会出现“找不到文件”的错误,它仍然只是上传到 root!

谷歌Documentation 说我所做的应该有效。我错过了什么?

如果有帮助,请在此处查看完整代码:

html:

<html>
  <head>
    <base target="_top">
      <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script> 

    
    <title>Upload Files</title>
  </head>
  <body>
    <div class="container-fluid mt-5 d-flex justify-content-center">
		
	
	<div class="card py-2 px-5" style="width:550px">
		
			<div class="card-head my-4 border-bottom border-top">
				<h4 class="card-title">Upload File</h4>
			</div></center>
		
		<div class="card-body" id="resp">
			<form id="fileuploadForm">
			
				 <li class="list-group-item"><input type="file" name="myFile" id="uploadfile"></li>
			
		</form>
		</div>
        <center><div id="progress" class="display-4"></div></center>

	</div>
	



		
	</div>

<script>
    const chunkSize = 5242880;

    $('#uploadfile').on("change", function() {
        var file = this.files[0];
        if (file.name != "") {
            var fr = new FileReader();
            fr.fileName = file.name;
            fr.fileSize = file.size;
            fr.fileType = file.type;
            fr.onload = init;
            fr.readAsArrayBuffer(file);
        }
    });

    function init() {
        $("#progress").text("Initializing.");
        var fileName = this.fileName;
        var fileSize = this.fileSize;
        var fileType = this.fileType;
        console.log({fileName: fileName, fileSize: fileSize, fileType: fileType});
        var buf = this.result;
        var chunkpot = getChunkpot(chunkSize, fileSize);
        var uint8Array = new Uint8Array(buf);
        var chunks = chunkpot.chunks.map(function(e) {
            return {
                data: uint8Array.slice(e.startByte, e.endByte + 1),
                length: e.numByte,
                range: "bytes " + e.startByte + "-" + e.endByte + "/" + chunkpot.total,
            };
        });
        google.script.run.withSuccessHandler(function(at) {
            var xhr = new XMLHttpRequest();
            xhr.open("POST", "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&addParents=1XVNmZ7Q4vHLB7p9tTk0_rz7Z1VOwy_I8&removeParents=root");
            xhr.setRequestHeader('Authorization', "Bearer " + at);
            xhr.setRequestHeader('Content-Type', "application/json");
            xhr.send(JSON.stringify({
                mimeType: fileType,
                name: fileName,
            }));
            xhr.onload = function() {
                doUpload({
                    location: xhr.getResponseHeader("location"),
                    chunks: chunks,
                });
            };
            xhr.onerror = function() {
                console.log(xhr.response);
            };
        }).getAt();
    }

    function doUpload(e) {
        var chunks = e.chunks;
        var location = e.location;
        console.log("this stuff: " + location);
        var cnt = 0;
        var end = chunks.length;
        var temp = function callback(cnt) {
            var e = chunks[cnt];
            var xhr = new XMLHttpRequest();
            xhr.open("PUT", location, true);
            xhr.setRequestHeader('Content-Range', e.range);
            xhr.send(e.data);
            xhr.onloadend = function() {
                var status = xhr.status;
                cnt += 1;
                console.log("Uploading: " + status + " (" + cnt + " / " + end + ")");
                $("#progress").text("Uploading: " + Math.floor(100 * cnt / end) + "%");
                if (status == 308) {
                    callback(cnt);
                    
                } else if (status == 200) {
                    $("#progress").text("Done.");
                } else {
                    $("#progress").text("Error: " + xhr.response);
                }
            };
        }(cnt);
    }

    function getChunkpot(chunkSize, fileSize) {
        var chunkPot = {};
        chunkPot.total = fileSize;
        chunkPot.chunks = [];
        if (fileSize > chunkSize) {
            var numE = chunkSize;
            var endS = function(f, n) {
                var c = f % n;
                if (c == 0) {
                    return 0;
                } else {
                    return c;
                }
            }(fileSize, numE);
            var repeat = Math.floor(fileSize / numE);
            for (var i = 0; i <= repeat; i++) {
                var startAddress = i * numE;
                var c = {};
                c.startByte = startAddress;
                if (i < repeat) {
                    c.endByte = startAddress + numE - 1;
                    c.numByte = numE;
                    chunkPot.chunks.push(c);
                } else if (i == repeat && endS > 0) {
                    c.endByte = startAddress + endS - 1;
                    c.numByte = endS;
                    chunkPot.chunks.push(c);
                }
            }
        } else {
            var chunk = {
                startByte: 0,
                endByte: fileSize - 1,
                numByte: fileSize,
            };
            chunkPot.chunks.push(chunk);
        }
        return chunkPot;
    }
    



    
</script>
</body>

</html>

GS:

function doGet() {
  return HtmlService.createHtmlOutputFromFile("index.html");
}

function getAt() {
  return ScriptApp.getOAuthToken();
}

// This commented line is used for enabling Drive API and adding a scope of "https://www.googleapis.com/auth/drive".
// So please don't remove this.
// DriveApp.createFile();

【问题讨论】:

  • 我必须为我糟糕的英语水平道歉。我无法理解I also tried changing the PUT request for the "resuming" but It either errors out with "file not found" if I dont match the original POST or if I do make them the same, It still just uploads to root!。可以问一下它的详细情况吗?顺便说一句,如果您从其他站点或存储库中引用了脚本,我可以问您吗?
  • 感谢@Tanaike - 在回答您的第一个问题时,我很确定正在发生的是第一个函数通过执行 XMLHttpRequest.open 设置上传并使用 POST 作为方法。它上传一个块,然后用另一个 XMLHttpRequest.open 更新文件,但这次 PUT 作为方法(根据关于“更新现有文件”的 Google API 说明)link 为什么我不问编写代码的人?我只是打开了太多标签并关闭了错误的标签......现在我找不到它的位置:(
  • 感谢您的回复。如果您的脚本修改了原始脚本并出现问题,则可以通过比较它们来了解问题的原因。所以我询问了原始脚本的源站点。这个怎么样?
  • 我对原始文件的唯一更改是 HTML 以使其看起来更漂亮 - 当然,我使用的是我自己的文件夹 ID。原始文件并没有尝试选择文件夹,它只是为了绕过 Google 的 50mb 限制 - 所以唯一不同的是我试图将 addParents=1234.... 包含到我的POST 或 PUT 中的 URL。原始代码的 URL 直接在以下结尾:uploadType=resumable
  • “如果您的脚本修改了原始脚本并出现问题” 这是一个很好的观点,我知道您来自哪里,但我的更改确实不是问题做任何事情......它只是像原始脚本一样继续工作并上传到根文件夹。也许我不明白 Google 的 API 关于 addParents 的说法

标签: javascript html google-apps-script google-drive-api xmlhttprequest


【解决方案1】:

我相信你的目标如下。

  • 您想通过可恢复上传将文件上传到 Google Drive 上的特定文件夹。
  • 您希望使用 Javascript 通过从 Google Apps 脚本检索访问令牌来实现此目的。

对于这个,这个答案怎么样?

修改点:

  • 从您的回复评论中,我可以注意到您使用了我的脚本。在这种情况下,我已经更新了脚本。在当前阶段,我发布了一个 Javascript 库,用于通过可恢复上传来上传文件。在这个答案中,我使用了这个。

修改后的脚本:

HTML 和 Javascript 端 (index.html):

<form><input name="file" id="uploadfile" type="file" /></form>
<div id="progress"></div>

<script src="https://cdn.jsdelivr.net/gh/tanaikech/ResumableUploadForGoogleDrive_js@master/resumableupload_js.min.js"></script>

<script>
  document.getElementById("uploadfile").addEventListener("change", run, false);

  function run(obj) {
    google.script.run
      .withSuccessHandler(accessToken =>
        ResumableUploadForGoogleDrive(accessToken, obj)
      )
      .getAuth();
  }

  function ResumableUploadForGoogleDrive(accessToken, obj) {
    const file = obj.target.files[0];
    if (file.name != "") {
      let fr = new FileReader();
      fr.fileName = file.name;
      fr.fileSize = file.size;
      fr.fileType = file.type;
      fr.accessToken = accessToken;
      fr.readAsArrayBuffer(file);
      fr.onload = resumableUpload;
    }
  }

  function resumableUpload(e) {
    document.getElementById("progress").innerHTML = "Initializing.";
    const f = e.target;
    const resource = {
      fileName: f.fileName,
      fileSize: f.fileSize,
      fileType: f.fileType,
      fileBuffer: f.result,
      accessToken: f.accessToken,
      folderId: "###"  // <--- Please set the folder ID.
    };
    const ru = new ResumableUploadToGoogleDrive();
    ru.Do(resource, function(res, err) {
      if (err) {
        console.log(err);
        return;
      }
      console.log(res);
      let msg = "";
      if (res.status == "Uploading") {
        msg =
          Math.round(
            (res.progressNumber.current / res.progressNumber.end) * 100
          ) + "%";
      } else {
        msg = res.status;
      }
      document.getElementById("progress").innerText = msg;
    });
  }
</script>
  • 在这种情况下,请将文件夹ID设置为resource

Google Apps 脚本端 (Code.gs):

function getAuth() {
  // DriveApp.createFile(blob) // This is used for adding the scope of "https://www.googleapis.com/auth/drive".
  return ScriptApp.getOAuthToken();
}

function showSidebar() {
  var html = HtmlService.createHtmlOutputFromFile("index");
  SpreadsheetApp.getUi().showSidebar(html);
}

参考:

补充:

如果要修改your current script,请修改如下。在可恢复上传的情况下,文件元数据在初始请求时设置。 Ref

发件人:

xhr.send(JSON.stringify({
    mimeType: fileType,
    name: fileName,
}));

收件人:

xhr.send(JSON.stringify({
    mimeType: fileType,
    name: fileName,
    parents: ["###"]  // <--- Please set the folder ID.
}));

【讨论】:

  • 100% 完美 - 这是一个很好的答案!示例、选项、解释。您是 Stack Overflow 社区的功劳!
  • @Different Gravity 感谢您的回复。我很高兴你的问题得到了解决。也谢谢你。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多