【问题标题】:How to make sure that the file is downloaded and then return a response to client side?如何确保文件已下载,然后向客户端返回响应?
【发布时间】:2019-11-14 03:29:58
【问题描述】:

我必须使用 nodejs 下载一个 json 文件并做出反应。 因此,当用户单击下载按钮时,会向 nodejs 发送一个请求,然后我发出一个 http 请求,传递用户、传递和 url 以在服务器上下载 json 文件,然后在客户端下载该文件

服务器端的文件下载需要将近5分钟,问题是我在下载文件完成之前遇到网络错误:

GET http://127.0.0.1:4000/downloadFile net::ERR_EMPTY_RESPONSE
Error: Network Error
    at createError (createError.js:17)
    at XMLHttpRequest.handleError (xhr.js:80)

当点击下载按钮时,反应端的这个函数被调用:

downloadFile() {
    this.setState({ buttonDownloadText: 'Wait ...' })
    axios.get(process.env.REACT_APP_HOST + '/downloadFile')
      .then(resp => {            
        window.open(process.env.REACT_APP_HOST + '/download?namefile=' + resp.data.fileName, '_self')           

        this.setState({ buttonDownloadText: 'Start download' })    

      })
      .catch((error) => {
        console.log(error) //here is where the Network Error is returned
        this.setState({ buttonDownloadText: 'Error. Try again' })
      })
  }

服务器端:

  const http = require('http');
  const fs = require('fs');

    app.get('/downloadFile', function (req, res, next) {
        try {
            const headers = new Headers();
            const URL = env.URL_API + '/api/downloadFile'
            const DOWNLOADURL = env.URL_API + '/api/download'
            headers.set('Authorization', 'Basic ' + base64.encode(username + ":" + password));

            fetch(URL, {
                method: 'GET',
                headers: headers,
            })
                .then(r => r.json())
                .then(data => 
                    fetch(DOWNLOADURL + '/' + data.fileName, {
                        method: 'GET',
                        headers: headers,
                    }).then(result => {   
                                const url = 'http://user:pass@url' + data.fileName
                                const arq = __dirname + '/download/' + data.fileName

                                var file = fs.createWriteStream(arq);
                                http.get(url, function (response) {
                                    response.pipe(file);
                                    file.on('finish', () => file.close(() => res.status(200).send({ file: data.fileName });)) //I have to return the file name to react function
                                });


                            }).catch(error => {
                                logger.error(`${error}`);
                            })
                    })
                        .catch(error => {
                            logger.error(`${error.status || 500} - ${error} - ${req.originalUrl} - ${req.method} - ${req.ip}`);
                            res.sendStatus(500)
                        })
               ....
    });

downloadFile函数必须返回文件名,所以我调用下载路由在clint端进行下载

app.get('/download', function (req, res) {
    try {
        const file = __dirname + '/download/' + req.query.namefile;
        res.download(file);
    } catch (error) {
        logger.error(`${error.status || 500} - ${error} - ${req.originalUrl} - ${req.method} - ${req.ip}`);
    }
});

所以,我的意思是,为什么我的downloadFile 路由没有等待下载文件完成返回它res.status(200).send({ file: data.fileName })?

【问题讨论】:

  • 很遗憾,如果接收服务器响应的时间过长,客户端通常会返回超时错误。我建议使用不同的方法,例如将文件上传到云存储桶(例如 AWS S3 或 Firebase)并将链接返回给客户端。
  • @Michele 是否无法避免来自客户端的超时错误?
  • 据我所知,没有办法强制浏览器等待响应。此外,您不能依赖浏览器。除此之外,向客户端提供静态 URL 是一个好习惯,因为它可以在旅途中暂停/恢复下载过程。
  • 我明白了。 Tku 这么多!

标签: node.js reactjs fs


【解决方案1】:

由于不能强迫浏览器等待服务器的长时间响应,我建议采用不同的方法:将文件上传到云端(例如 AWS 或 Firebase),然后将链接返回给客户端。

如果使用 Firebase(或其他实时数据库),服务器可以在文件上传完成时在数据库中设置一个标志。因此,当文件可供下载时,客户端会收到异步通知。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-11
    • 1970-01-01
    • 2017-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多