使用API和Node.js无法从Google Drive下载文件

6

我正在尝试使用node.js和Google SDK API从谷歌云端硬盘下载文件。

但是我无法在服务器端保存/写入文件。

代码:

var GoogleTokenProvider = require("refresh-token").GoogleTokenProvider,
    async = require('async'),
    fs = require("fs"),
    request = require('request'),
    _accessToken;

var _XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var https = require('https');

const CLIENT_ID = "";
const CLIENT_SECRET = "";
const REFRESH_TOKEN = '';
const ENDPOINT_OF_GDRIVE = 'https://www.googleapis.com/drive/v2';

async.waterfall([
        //-----------------------------
        // Obtain a new access token
        //-----------------------------
        function(callback) {
            var tokenProvider = new GoogleTokenProvider({
                'refresh_token': REFRESH_TOKEN,
                'client_id': CLIENT_ID,
                'client_secret': CLIENT_SECRET
            });
            tokenProvider.getToken(callback);
        },

        //--------------------------------------------
        // Retrieve the children in a specified folder
        // 
        // ref: https://developers.google.com/drive/v2/reference/files/children/list
        //-------------------------------------------
        function(accessToken, callback) {
            _accessToken = accessToken;
            request.get({
                'url': ENDPOINT_OF_GDRIVE + '/files?' + "q='root' in parents  and (mimeType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document')",
                'qs': {
                    'access_token': accessToken
                }
            }, callback);
        },

        //----------------------------
        // Parse the response
        //----------------------------
        function(response, body, callback) {
            var list = JSON.parse(body);
            if (list.error) {
                return callback(list.error);
            }
            callback(null, list.items[0]);
        },

        //-------------------------------------------
        // Get the file information of the children.
        //
        // ref: https://developers.google.com/drive/v2/reference/files/get
        //-------------------------------------------
        function(children, callback) {

            var xhr = new _XMLHttpRequest();
            xhr.open('GET', children.downloadUrl);
            xhr.setRequestHeader('Authorization', 'Bearer ' + _accessToken);
            xhr.onload = function() {
                console.log("xhr.responseText", xhr.responseText)
                fs.writeFile("download.docx", xhr.responseText)
                callback(xhr.responseText);
            };
            xhr.onerror = function() {
                callback(null);
            };
            xhr.send();
        }
    ],
    function(err, results) {
        if (!err) {
            console.log(results);
        }
    });

我在控制台中看到了以下信息: xhr.responseText的内容大致如下。
��▬h��↕E6M��~��33∟�9�� � �►��/2�:���♂�4��]�♀I�R���►
$SB6Q���c↔��H�=;+
���►q�3Tdכ��@!T��hEl_�|�I�↨��h(�^:▬�[h̓D♠��f���♠*���ݾ��M→
�1⌂♦"N�↑�o�]�7U$��A6����♠�W��k`�f▬♫��K�Z�^‼�0{<Z�▼�]F�����

                             ���J♥A♀��♣�a�}7�
"���H�w"�♥���☺w♫̤ھ�� �P�^����O֛���;�<♠�aYՠ؛`G�kxm��PY�[��g
Gΰino�/<���<�1��ⳆA$>"f3��\�ȾT��∟I S�������W♥����Y

请帮我了解从Drive API获取的数据格式,并将其写成哪种格式,以便我得到完整的.docx文件。
编辑:
如果使用除xmlRequest之外的任何方法都可以帮助我下载文件(.docx)。

1
你的首要任务应该是找出如何将数据作为缓冲区返回,因为一旦二进制数据转换为字符串,数据就会变得损坏。使用Request.js模块,你需要在请求选项中设置编码:null。但是对于xhr,不确定。 - Robert Rossmann
你期望的输出是什么?downloadUrl 仅用于非 Google 文件。如果是 Google 文档,则将使用其中一个 exportLinks。 - pinoyyid
1
@pinoyyid,无论输出结果如何,我的目标是以正确的格式写入/保存/下载数据,在我的情况下是.docx文件,并在将其下载到服务器后编辑该.docx文件。 - Harpreet Singh
根据OP的示例,我认为它们不是谷歌文件,而是“application/vnd.openxmlformats-officedocument.wordprocessingml.document”。 - Gaurav Ramanan
如果您正在使用XMLHttpRequest,根据https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data上的说明,您需要设置`responseType =“arraybuffer”`。 - Drazisil
3个回答

10
node-XMLHttpRequest 似乎不支持二进制下载 - 可查看此问题。您所看到的是文件的二进制内容转换为字符串,这在JavaScript中是一种不可逆且对二进制数据有破坏性的过程(这意味着您无法将字符串转换回缓冲区并获得与原始内容相同的数据)。
使用request,您可以以如下方式下载二进制文件:
var request = require('request')
  , fs = require('fs')

request.get(
  { url: 'your-file-url'
  , encoding: null    // Force Request to return the data as Buffer
  , headers:
    { Authorization: 'Bearer ' + accessTokenHere
    }
  }
, function done (err, res) {
    // If all is well, the file will be at res.body (buffer)
    fs.writeFile('./myfile.docx', res.body, function (err) {
      // Handle err somehow
      // Do other work necessary to finish the request
    })
  }
)

注意:这会将整个文件缓冲到内存中,然后才能保存到磁盘。对于小文件来说,这没问题,但对于大文件,您可能需要将其实现为流式下载。 这个SO问题已经回答了这个问题,我建议您去看一下。

有关如何授权您的请求的更多信息,请参见Google Developers文档


我得到了401错误,这可能是因为我没有传递accessToken。在请求中,我应该在哪里添加accessTOken? - Harpreet Singh
不确定 - 请参见注释1。 - Robert Rossmann
好的,谢谢你的帮助,让我解决一下身份验证问题,然后我会回来找你。 - Harpreet Singh
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Harpreet Singh
让我们在聊天中继续这个讨论 - Harpreet Singh
显示剩余4条评论

2

完整的工作示例:从GoogleDrive下载文件 - Node.js API

var GoogleTokenProvider = require("refresh-token").GoogleTokenProvider,
    async = require('async'),
    fs = require("fs"),
    request = require('request'),
    _accessToken;

const CLIENT_ID = "";
const CLIENT_SECRET = "";
const REFRESH_TOKEN = '';
const ENDPOINT_OF_GDRIVE = 'https://www.googleapis.com/drive/v2';

async.waterfall([
        //-----------------------------
        // Obtain a new access token
        //-----------------------------
        function(callback) {
            var tokenProvider = new GoogleTokenProvider({
                'refresh_token': REFRESH_TOKEN,
                'client_id': CLIENT_ID,
                'client_secret': CLIENT_SECRET
            });
            tokenProvider.getToken(callback);
        },

        //--------------------------------------------
        // Retrieve the children in a specified folder
        // 
        // ref: https://developers.google.com/drive/v2/reference/files/children/list
        //-------------------------------------------
        function(accessToken, callback) {
            _accessToken = accessToken;
            request.get({
                'url': ENDPOINT_OF_GDRIVE + '/files?' + "q='root' in parents  and (mimeType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document')",
                'qs': {
                    'access_token': accessToken
                }
            }, callback);
        },

        //----------------------------
        // Parse the response
        //----------------------------
        function(response, body, callback) {
            var list = JSON.parse(body);
            if (list.error) {
                return callback(list.error);
            }
            callback(null, list.items);
        },

        //-------------------------------------------
        // Get the file information of the children.
        //
        // ref: https://developers.google.com/drive/v2/reference/files/get
        //-------------------------------------------
        function(children, callback) {

            for(var i=0;i<children.length;i++) {
                var file = fs.createWriteStream(children[i].title);
                // Downnload and write file from google drive
                (function(child) {
                    request.get(
                      { url: child.downloadUrl
                      , encoding: null    // Force Request to return the data as Buffer
                      , headers:
                        { Authorization: 'Bearer ' + _accessToken
                        }
                      }
                    , function done (err, res) {
                        res.pipe(file)
                        // If all is well, the file will be at res.body (buffer)
                        fs.writeFile('./' + child.title, res.body, function (err) {
                            if(!err) {
                                console.log('done')
                            } else {
                                console.log(err)
                            }
                          // Handle err somehow
                          // Do other work necessary to finish the request
                        })
                      }
                    )

                })(children[i])
            }
        }
    ],
    function(err, results) {
        if (!err) {
            console.log(results);
        }
    });

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接