【问题标题】:GET request from browser works to download file to local but XMLHttpRequest Javascript script does not download file来自浏览器的 GET 请求可以将文件下载到本地,但 XMLHttpRequest Javascript 脚本不下载文件
【发布时间】:2018-08-02 04:40:19
【问题描述】:

我想我在使用 XMLHttpRequest 时遇到问题,当我导航到 localhost/dashboard/downloadfile?file-name=hw3.txt 时,文件会在本地下载,但如果我使用函数 checkDownload() 来启动 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);
  });
});

【问题讨论】:

  • 错误是什么?
  • 它不会抛出错误。客户端收到 200 响应,但没有下载任何文件。
  • 好的,那么你已经在xhr对象中有页面,在.status的if里面试试console.log(xhr.responseText)
  • Xhr 不会下载文件,你应该放一个链接或者其他东西
  • 文本文件在客户端控制台中正确打印,但下载仍然无法正常工作。我不认为链接不起作用,因为我正在使用服务器上的数据库来获取 s3 存储桶中的用户文件的文件密钥,并且需要进行用户验证。如果有什么办法,我肯定会尝试,但我不明白它是如何工作的

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


【解决方案1】:

我让它工作了。我没有尝试从s3.getObject() 创建读取流,而是在服务器上生成了一个指向 s3 对象的签名 url 并将其返回给客户端,然后使用带有element.href = signedRequest 的“a”html 元素并使用 javascript 单击该元素.我遇到的新问题是,当最初上传 s3 对象时,我无法找到设置元数据的方法,我需要通过 aws 控制台手动更改单个 s3 对象的元数据,以便它有标题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);
}

更改的服务器代码:

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

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

    s3.getSignedUrl('getObject', s3Params, (err, data) => {
      if (err) {
        // eslint-disable-next-line
        console.log(err);
        res.end();
      }
      const returnData = {
        signedRequest: data,
        url: `https://${S3_BUCKET}.s3.amazonaws.com/${ keyres.rows[0].filekey }`,
      };
      res.write(JSON.stringify(returnData));
      res.end();
    });
  });
});

【讨论】:

    【解决方案2】:

    您正在从服务器返回一个 blob,因此为了下载您需要在xhr.status === 200 时执行一些操作。

    类似这样的:

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

    【讨论】:

    • 我收到一个错误:未捕获的类型错误:无法在“URL”上执行“createObjectURL”:找不到与提供的签名匹配的函数。在 XMLHttpRequest.xhr.onreadystatechange
    • 我是个白痴,显然你必须在某处声明 URL,但我无法弄清楚构造函数,因为我得到了一个流
    • 尝试使用window.URL。应该是一样的。 API 随浏览器一起提供。这是文档:developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
    • 试过了,还是不行。我在 xhr.status 为 200 之后放置了“console.log(xhr.repsonse)”,并且正确打印了 txt 文件但仍未下载到本地存储。它与服务器上的 createReadStream 有关吗?我是否必须将其转换为 Blob 可以读取的内容?
    • 只是为了确定。你想下载到本地磁盘吗?而不是本地存储?顺便说一句,您在评论中的 console.log 中有错字。您的服务器应该没问题,因为您可以直接在 url 上下载。
    【解决方案3】:

    要下载具有 URL,您可以使用属性 downloada 标签:

    <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()

    但该代码不会下载文件。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多