来自浏览器的GET请求可将文件下载到本地,但XMLHttpRequest Javascript脚本无法下载文件

时间:2018-07-30 18:24:00

标签: javascript node.js express xmlhttprequest aws-sdk

我在使用XMLHttpRequest时遇到麻烦,当我导航到localhost / dashboard / downloadfile?file-name = hw3.txt时,文件在本地下载,但是如果我使用checkFile()函数启动XMLHttpRequest,文件会无法下载。

这是我的客户代码:

function checkDownload() {
  const filename = "hw3.txt";
  const xhr = new XMLHttpRequest();
  xhr.responseType = "blob";
  xhr.open('GET', `/dashboard/downloadfile?file-name=${ filename }`);
  xhr.onreadystatechange = () => {
    if(xhr.readyState === 4) {
      if(xhr.status === 200) {

      }
    }
  }
  xhr.send();
}

然后这是我的服务器代码:

app.get('/dashboard/downloadfile', requiresLogin, (req, res) => {
  const userid = req.user.id;
  const filename = req.query['file-name'];

  db.getFileKey([userid, filename], (keyres) => {
    const params = {
      Bucket: S3_BUCKET,
      Key: keyres.rows[0].filekey,
    };


    res.setHeader('Content-disposition', `attachment; filename=${ filename }`);
    res.setHeader('Content-type', `${ mime.getType(keyres.rows[0].filetype) }`);
    s3.getObject(params, (awserr, awsres) => {
      if(awserr) console.log(awserr);
      else console.log(awsres);
    }).createReadStream().pipe(res);
  });
});

3 个答案:

答案 0 :(得分:0)

您正在从服务器获取一个Blob,因此要下载文件,您需要在xhr.status === 200时执行一些操作。

类似这样的东西:

...
if(xhr.status === 200) {
   var fileUrl = URL.createObjectURL(xhr.responseText)
   window.location.replace(fileUrl)
}
...  

答案 1 :(得分:0)

要下载具有URL的网址,可以使用download标签的属性a

<a download="something.txt" href="https://google.com">Download Google</a>

如果使用xhr.responseType = "blob",则必须执行以下操作:

function checkDownload() {
  const filename = "hw3.txt";
  const xhr = new XMLHttpRequest();
  xhr.responseType = "blob";
  xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1');
  xhr.onreadystatechange = () => {
    if(xhr.readyState === 4) {
      if(xhr.status === 200) {      
      var reader = new FileReader();
      reader.readAsArrayBuffer(xhr.response);      
      reader.addEventListener("loadend", function() {  
       var a = new Int8Array(reader.result);
      console.log(JSON.stringify(a, null, '  '));
});
      }
    }
  }
  xhr.send();
}

checkDownload()

但是该代码不会下载文件。

答案 2 :(得分:0)

我知道了。我没有尝试从library创建读取流,而是生成了指向服务器上s3对象的签名url,然后将其返回给客户端,然后使用带有s3.getObject()的'a'html元素并使用了javascript单击该元素。我遇到的新问题是,在最初上传s3对象时,我想不出一种方法来设置它的元数据,我需要通过aws控制台手动更改单个s3对象上的元数据,以便它具有标题element.href = signedRequest

更改了客户代码:

Content-Disposition: attachment; filename=${ filename }

更改了服务器代码:

function initDownload(filename) {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', `/sign-s3-get-request?file-name=${ filename }`);
  xhr.onreadystatechange = () => {
    if(xhr.readyState === 4) {
      if(xhr.status === 200) {
        const response = JSON.parse(xhr.responseText);
        startDownload(response.signedRequest, response.url);
      }
    }
  }
  xhr.send();
}

function startDownload(signedRequest, url) {
  var link = document.createElement('a');
  link.href = signedRequest;
  link.setAttribute('download', 'download');
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
}