简体   繁体   English

将 ReactJS 对象下载为文件

[英]Download a ReactJS object as a file

I am building an application with a ReactJS front end that connects to an Express API server.我正在构建一个带有连接到 Express API 服务器的 ReactJS 前端的应用程序。 Calls to the API are made using Ajax.对 API 的调用是使用 Ajax 进行的。

In one of my views, a table loads with "Export" links on each row.在我的一个视图中,一个表格加载了每一行的“导出”链接。 The Export links lead to a React route that calls an API endpoint which provides a CSV file to download.导出链接通向调用 API 端点的 React 路由,该 API 端点提供要下载的 CSV 文件。

If I hit the API endpoint directly with a valid request (outside the React app), a file download is initiated in my browser.如果我使用有效请求(在 React 应用程序之外)直接访问 API 端点,则会在我的浏览器中启动文件下载。 Perfect!完美的! However, following the Export link from the React page attempts to load the view where the call to the API occurs.但是,遵循 React 页面中的导出链接会尝试加载调用 API 的视图。 The table disappears from the view and is replaced by the file contents (on purpose to prove I have the data) but no file is downloaded.该表从视图中消失并被文件内容替换(为了证明我有数据),但没有下载文件。

Can I force a download of the contents of the response object as a file?我可以强制将响应对象的内容下载为文件吗? Could this take place in the ajax success callback?这会发生在 ajax 成功回调中吗? I made an attempt with javascript but I'm struggling with the React virtual DOM... I assume this must be pretty straight forward but I'm stumped.我尝试使用 javascript,但我在 React 虚拟 DOM 上挣扎......我认为这一定很简单,但我很难过。

EDIT: Comments by @Blex helped me solve this issue!编辑:@Blex 的评论帮助我解决了这个问题! The solution is added to the code snippet...解决方案已添加到代码片段中...

Here is the JSX that receives the data:这是接收数据的 JSX:

module.exports = React.createClass({

    mixins: [Router.State],
    getInitialState: function() {
        return {
            auth: getAuthState(),
            export: [],
            passedParams: this.getParams()
        };
    },

    componentDidMount: function(){
        $.ajax({
            type: 'GET',
            url: ''+ API_URL +'/path/to/endpoint'+ this.state.passedParams.id +'/export',
            dataType: 'text',
            headers: {
                'Authorization': 'Basic ' + this.state.auth.base + ''
            },
            success: function (res) {
                // can I force a download of res here?
                console.log('Export Result Success -- ', res);
                if(this.isMounted()){
                    console.log('Export Download Data -- ', res);
                    this.setState({export: res[1]});
                    // adding the next three lines solved my problem
                    var data = new Blob([res], {type: 'text/csv'});
                    var csvURL = window.URL.createObjectURL(data);
                    //window.open(csvURL);
                    // then commenting out the window.open & replacing
                    // with this allowed a file name to be passed out
                    tempLink = document.createElement('a');
                    tempLink.href = csvURL;
                    tempLink.setAttribute('download', 'filename.csv');
                    tempLink.click();
                }
            }.bind(this),
            error: function (data) {
                console.log('Export Download Result Error -- ', data);
            }
        });
    },

    render: function(){
        console.log('exam assignment obj -- ', this.state.passedParams.name);
        var theFileContents = this.state.export;
            return(
            <div className="row test-table">
                <table className="table" >
                    <tr className="test-table-headers">
                    {theFileContents} // this loads the contents
                    // can I auto download theFileContents?
                    </tr>
                </table>
            </div>
            )
    }
});

Adding the following code based on comments by @blex got the file download working. 根据@blex的注释添加以下代码使文件下载工作。 To see it in context, take a look at the success callback in the question. 要在上下文中查看它,请查看问题中的成功回调。

var data = new Blob([res], {type: 'text/csv'});
var csvURL = window.URL.createObjectURL(data);
tempLink = document.createElement('a');
tempLink.href = csvURL;
tempLink.setAttribute('download', 'filename.csv');
tempLink.click();

I used a package jsonexport in my React app and now I am able to download the csv file on a link click. 我在我的React应用程序中使用了一个包jsonexport ,现在我可以在链接点击上下载csv文件了。 Here is what I did: 这是我做的:

.
.
import React, {useState,useEffect} from 'react';// I am using React Hooks
import * as jsonexport from "jsonexport/dist";
.
.
.
const [filedownloadlink, setFiledownloadlink] = useState("");//To store the file download link

.
.
.

Create a function that will provide data for CSV. 创建一个为CSV提供数据的功能。 It can also be in a callback from a network request. 它也可以来自网络请求的回调。 When this method is called, it will set value in filedownloadlink state. 调用此方法时,它将在filedownloadlink状态中设置值。

function handleSomeEvent(){
var contacts = [{
        name: 'Bob',
        lastname: 'Smith'
    },{
        name: 'James',
        lastname: 'David'
    },{
        name: 'Robert',
        lastname: 'Miller' 
    },{
        name: 'David',
        lastname: 'Martin'
    }];

    jsonexport(contacts,function(err, csv){
        if(err) return console.log(err);
        var myURL = window.URL || window.webkitURL //window.webkitURL works in Chrome and window.URL works in Firefox
        var csv = csv;  
        var blob = new Blob([csv], { type: 'text/csv' });  
        var csvUrl = myURL.createObjectURL(blob);
        setFiledownloadlink(csvUrl);
    });
}

In the render function use something like this: 在render函数中使用类似这样的东西:

{filedownloadlink &&<a download="UserExport.csv" href={filedownloadlink}>Download</a>}

The above link will be visible when filedownloadlink has some data to download. filedownloadlink有一些要下载的数据时,上面的链接将是可见的。

Adding the below code for future reference.添加以下代码以供将来参考。 This is to include some additional checks on browser compatibility and additional code to include IE10+.这是为了包括对浏览器兼容性的一些额外检查,以及包含 IE10+ 的额外代码。

/* Take a blob and force browser to click a link and save it from a download path
     * log out timing
     *
     * @param {Blob}
     * @method saveFile
     */
    function saveFile(blob) {
        const uniqTime = new Date().getTime();
        const filename = `my_file_${uniqTime}`;

        if (navigator.msSaveBlob) { // IE 10+
            console.info('Starting call for ' + 'ie download');
            const csvFormatTimeStart = new Date().getTime();

            const ieFilename = `${filename}.csv`;
            navigator.msSaveBlob(blob, ieFilename);

            const csvFormatTimeEnd = new Date().getTime();
            const csvFormatTime = csvFormatTimeEnd - csvFormatTimeStart;
            console.log('ie download takes ' + csvFormatTime + ' ms to run');
        } else {
            console.info('Starting call for ' + 'regular download');
            const csvFormatTimeStart = new Date().getTime();
            let link = document.createElement("a");
            if (link.download !== undefined) { // feature detection
                // Browsers that support HTML5 download attribute
                var url = URL.createObjectURL(blob);
                link.setAttribute("href", url);
                link.setAttribute("download", filename);
                link.style.visibility = 'hidden';
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
            }

            const csvFormatTimeEnd = new Date().getTime();
            const csvFormatTime = csvFormatTimeEnd - csvFormatTimeStart;
            console.log('regular download takes ' + csvFormatTime + ' ms to run');
        }

        clickEnd = new Date().getTime();
        console.log('The whole process took: ' + (clickEnd - clickStart) + ' ms');
    }

Credit should go to this article .信用应该去这篇文章

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM