Dropbox 直接从浏览器上传文件

Posted

技术标签:

【中文标题】Dropbox 直接从浏览器上传文件【英文标题】:Dropbox direct upload files from browser 【发布时间】:2016-03-27 17:25:58 【问题描述】:

我正在尝试 [从浏览器/Web 应用程序] 将文件直接上传到保管箱,代码 API 上的“uploadFile”功能需要将文件上传到服务器上,这给我带来了麻烦,因为我这样做了不希望将任何文件上传到我的服务器并从那里上传到保管箱。

$f = fopen("test.jpg", "rb"); // requires file on server
$result = $dbxClient->uploadFile("test.jpg", dbx\WriteMode::add(), $f);
fclose($f);

试过这个https://github.com/dropbox/dropbox-js很失望,说没有明确的文档,文档部分的很多链接都坏了。

我需要将文件上传到我的帐户,客户无需登录到保管箱。

任何指针将不胜感激。寻找 Ajax / javascript 方法。

更新

我尝试了以下方法,但 Dropbox 没有响应

html

<input type="file" name="file" id="file" onchange="doUpload(event)">

JavaScript

var doUpload = function(event)

var input = event.target;
var reader = new FileReader();


  reader.onload = function()
    var arrayBuffer = reader.result;

   $.ajax(  
    url: "https://api-content.dropbox.com/1/files_put/auto/uploads/" + input.files[0].name,  
    headers:   
        Authorization: 'Bearer ' + MyAccessToken,  
        contentLength: file.size  
    ,  
    crossDomain: true,  
    crossOrigin: true,  
    type: 'PUT',  
    contentType: input.files[0].type,  
    data: arrayBuffer,  
    dataType: 'json',  
    processData: false,
    success : function(result) 
        $('#uploadResults').html(result);
    
    );
  
 reader.readAsArrayBuffer(input.files[0]);

【问题讨论】:

不久前我遇到了 Dropbox API 文档的问题。用户 @smarx 为 Dropbox 工作,并渴望听到用户对文档的看法。 这段代码看起来很合理。当您说“Dropbox 没有响应”时,您真的是指对 HTTP 请求没有响应吗? (您可以在浏览器的网络选项卡中查看。)API 返回错误的可能性更大。 跳出两个可能的问题:contentLength 是一个虚假的标头名称(尽管我怀疑这会破坏任何东西),除非定义了变量 file,否则file.size 可能不起作用某处。 【参考方案1】:

Dropbox 刚刚发布了一个博客,其中包含有关如何执行此操作的说明。你可以在https://blogs.dropbox.com/developers/2016/03/how-formio-uses-dropbox-as-a-file-backend-for-javascript-apps/ 找到它(完全公开,我写了这篇博文。)

这是上传文件的方法。

/**
 * Two variables should already be set.
 * dropboxToken = OAuth token received then signing in with OAuth.
 * file = file object selected in the file widget.
 */

var xhr = new XMLHttpRequest();

xhr.upload.onprogress = function(evt) 
    var percentComplete = parseInt(100.0 * evt.loaded / evt.total);
    // Upload in progress. Do something here with the percent complete.
;

xhr.onload = function() 
    if (xhr.status === 200) 
        var fileInfo = JSON.parse(xhr.response);
        // Upload succeeded. Do something here with the file info.
    
    else 
        var errorMessage = xhr.response || 'Unable to upload file';
        // Upload failed. Do something here with the error.
    
;

xhr.open('POST', 'https://content.dropboxapi.com/2/files/upload');
xhr.setRequestHeader('Authorization', 'Bearer ' + dropboxToken);
xhr.setRequestHeader('Content-Type', 'application/octet-stream');
xhr.setRequestHeader('Dropbox-API-Arg', JSON.stringify(
    path: '/' +  file.name,
    mode: 'add',
    autorename: true,
    mute: false
));

xhr.send(file);

然后从 Dropbox 下载文件。

var downloadFile = function(evt, file) 
  evt.preventDefault();
  var xhr = new XMLHttpRequest();
  xhr.responseType = 'arraybuffer';

  xhr.onload = function() 
    if (xhr.status === 200) 
      var blob = new Blob([xhr.response], type: ’application/octet-stream’);
      FileSaver.saveAs(blob, file.name, true);
    
    else 
      var errorMessage = xhr.response || 'Unable to download file';
      // Upload failed. Do something here with the error.
    
  ;

  xhr.open('POST', 'https://content.dropboxapi.com/2/files/download');
  xhr.setRequestHeader('Authorization', 'Bearer ' + dropboxToken);
  xhr.setRequestHeader('Dropbox-API-Arg', JSON.stringify(
    path: file.path_lower
  ));
  xhr.send();

FileSaver 和 Blob 无法在旧版浏览器上运行,因此您可以为它们添加解决方法。

正如其他答案所指出的,上传或下载文件的每个会话都需要访问保管箱令牌。将其他人的令牌发送给用户是一个安全问题,因为拥有令牌将使他们能够完全控制保管箱帐户。完成这项工作的唯一方法是让每个人都通过 Dropbox 进行身份验证并获得他们自己的令牌。

在Form.io,我们已经在我们的平台中实现了身份验证和上传/下载。这使得使用 Dropbox 作为文件后端构建 Web 应用程序变得非常容易。

【讨论】:

【参考方案2】:

“我需要将文件上传到我的帐户,客户无需登录到保管箱。”

那么您真的需要在服务器端进行上传。要做到这一点,客户端意味着将访问令牌发送到浏览器,此时您的应用程序的任何用户都可以使用该访问令牌对您的帐户做任何他们想做的事情。 (例如,删除所有其他文件,上传他们的私人 DVD 收藏等)

出于安全原因,我强烈建议您在服务器端进行上传,您可以将访问令牌保密。

【讨论】:

虽然确实可以将访问令牌保密,但也不建议先将较大的文件上传到服务器[> 100 mb],然后再从那里上传到保管箱。任何直接将文件上传到我的 Dropbox 帐户的方法都会很棒。 今晚研究了一下,好像S3为上传提供了逐项签名,这比分发你的令牌要安全得多【参考方案3】:

到目前为止给出的答案并没有使用 Dropbox javascript SDK,我认为这可能是最好的解决方法。在此处查看此链接:

https://github.com/dropbox/dropbox-sdk-js/blob/master/examples/javascript/upload/index.html

它提供了一个依赖于已下载 SDK 的示例。 (编辑:在使用 SDK 之后,我意识到它创建了一个类似于该线程中接受的答案的 POST 请求。但是,流行的答案忽略了一个 OPTIONS 预检调用,该调用是 sdk 在实际 POST 之前进行的)

我还可以补充一点,Dropbox sdk 示例中未显示的内容是您可以将 blob 对象上传到 Dropbox;例如,如果您想从画布中动态提取图像并上传它们,并且不想上传通过文件上传输入从文件系统中选择的内容,这很有用。

这是我所描述的场景的一个简短示例:

//code below after having included dropbox-sdk-js in your project.  
//Dropbox is in scope!
var dbx = new Dropbox.Dropbox( accessToken: ACCESS_TOKEN );
//numerous stack overflow examples on creating a blob from data uri
var blob = dataURIToBlob(canvas.toDataUrl());
//the path here is the path of the file as it will exist on dropbox.
//should be unique or you will get a 4xx error
dbx.filesUpload(path: `unq_filename.png`, contents: blob)

【讨论】:

你在这里错过了 /(slash):dbx.filesUpload(path: '/filename.png', contents: file)【参考方案4】:

非常感谢@smarx 的指点,我能够找到最终的解决方案。

我还添加了一些额外的功能,例如监听上传进度,以便向用户显示上传进度百分比。

HTML

<input type="file" name="file" id="file" onchange="doUpload(event)">

JavaScript

var doUpload = function(event)

      var input = event.target;
      var reader = new FileReader();


      reader.onload = function()
        var arrayBuffer = reader.result;
        var arrayBufferView = new Uint8Array( arrayBuffer );
        var blob = new Blob( [ arrayBufferView ],  type: input.files[0].type  );
        var urlCreator = window.URL || window.webkitURL;
        var imageUrl = urlCreator.createObjectURL( blob );   

        $.ajax(  
          url: "https://api-content.dropbox.com/1/files_put/auto/YourDirectory/" + input.files[0].name,  
          headers:   
            'Authorization':'Bearer ' +YourToken,  
            'Content-Length':input.files[0].size  
          ,  
          crossDomain: true,  
          crossOrigin: true,  
          type: 'PUT',  
          contentType: input.files[0].type,  
          data: arrayBuffer,  
          dataType: 'json',  
          processData: false,
          xhr: function()
          
            var xhr = new window.XMLHttpRequest();
           //Upload progress, litsens to the upload progress 
           //and get the upload status
           xhr.upload.addEventListener("progress", function(evt)
            if (evt.lengthComputable) 
              var percentComplete = parseInt( parseFloat(evt.loaded / evt.total) * 100);
              //Do something with upload progress
              $('#uploadProgress').html(percentComplete);
              $('#uploadProgressBar').css('width',percentComplete+'%');
             
            , false);
           ,
         beforeSend: function()
           // Things you do before sending the file 
           // like showing the loader GIF
         ,
         success : function(result) 
           // Display the results from dropbox after upload 
           // Other stuff on complete
          ,

        ); 
       
     reader.readAsArrayBuffer(input.files[0]);
    

您使用 PUT 方法作为我们唯一的目标是上传文件,根据我对各种资源的研究(*** 和 zacharyvoase),put 方法可以流式传输大文件,它的设计也是将文件放在指定的 URI ,如果文件存在,则必须替换文件。不能将 PUT 方法移动到指定 URL 以外的其他 URL。

风险

在客户端使用访问令牌有风险,需要采取高安全措施来屏蔽令牌。但现代 Web 开发工具(如浏览器控制台、Firebug 等)可以监控您的服务器请求并查看您的访问令牌。

【讨论】:

【参考方案5】:

上传.html

上传

上传.js

$('#form_wizard_1 .button-submit').click(function () 
      var ACCESS_TOKEN ="Your token get from dropbox";
      var dbx = new Dropbox( accessToken: ACCESS_TOKEN );
      var fileInput = document.getElementById('files1');
      var file = fileInput.files[0];
      res=dbx.filesUpload(path: '/' + file.name, contents: file)
      .then(function(response) 
      var results = document.getElementById('results');
      results.appendChild(document.createTextNode('File uploaded!'));
      res=response;
      console.log(response);
    )
    .catch(function(error) 
       console.error(error);
    );

【讨论】:

这将是一个很好的答案,但您需要包含 Dropbox 对象来自 SDK 的事实。此外,这基本上只是一个文档示例的剪切和粘贴。

以上是关于Dropbox 直接从浏览器上传文件的主要内容,如果未能解决你的问题,请参考以下文章

Dropbox.Api 无法上传大文件

使用 Dropbox API V2 + Cordova 将文件上传到 Dropbox

Dropbox网盘 文件上传过慢失败

通过 Dropbox API 上传的文件内容不一致

直接从浏览器将文件上传到 Azure Blob 存储?

有没有办法告诉 Dropbox 不要上传某个文件? (Eclipse 相关)