如何将React响应下载为文件

这是其中的代码 actions.js

export function exportRecordToExcel(record) {

return ({fetch}) => ({

type: EXPORT_RECORD_TO_EXCEL,

payload: {

promise: fetch('/records/export', {

credentials: 'same-origin',

method: 'post',

headers: {'Content-Type': 'application/json'},

body: JSON.stringify(data)

}).then(function(response) {

return response;

})

}

});

}

返回的响应是一个.xlsx文件。我希望用户能够将其保存为文件,但是什么也没有发生。我认为服务器返回的响应类型正确,因为在控制台中它说

Content-Disposition:attachment; filename="report.xlsx"

我想念的是什么?我应该在减速器中做什么?

回答:

浏览器技术当前不支持直接从Ajax请求下载文件。解决方法是添加一个隐藏的表单并将其提交到幕后,以使浏览器触发“保存”对话框。

我正在运行标准的Flux实现,因此不确定确切的Redux(Reducer)代码应该是什么,但是我刚刚为文件下载创建的工作流是这样的…

  1. 我有一个叫做的React组件FileDownload。该组件所做的全部工作就是呈现一个隐藏的表单,然后在内部componentDidMount立即提交表单并将其称为onDownloadCompleteprop。
  2. 我还有另一个React组件,我们称之为Widget下载按钮/图标(实际上很多…一个用于表中的每个项目)。Widget具有相应的操作和存储文件。Widget进口FileDownload
  3. Widget有两种与下载相关的方法:handleDownloadhandleDownloadComplete
  4. Widget商店有一个名为的属性downloadPathnull默认设置为。如果将其值设置为null,则不会进行任何文件下载,并且该Widget组件不会呈现该FileDownload组件。
  5. 单击中的按钮/图标Widget将调用handleDownload触发downloadFile动作的方法。该downloadFile操作不会发出Ajax请求。它将DOWNLOAD_FILE事件分发给商店,并随事件一起发送downloadPath文件下载。商店保存downloadPath并发出更改事件。
  6. 因为现在有downloadPathWidget将呈现FileDownload传递必要的道具包括downloadPath以及所述handleDownloadComplete方法作为值onDownloadComplete
  7. FileDownload被渲染和表单被提交method="GET"(POST应太)和action={downloadPath},服务器的响应现在将触发浏览器的保存对话框目标下载文件(在IE 9/10测试,最新的Firefox和Chrome)。
  8. 在表单提交之后,立即调用onDownloadComplete/ handleDownloadComplete。这将触发另一个调度DOWNLOAD_FILE事件的动作。但是,这次downloadPath设置为null。商店将另存为downloadPathnull并发出更改事件。
  9. 由于不再存在downloadPathFileDownload因此不会渲染组件Widget,因此世界是一个快乐的地方。

Widget.js-仅部分代码

import FileDownload from './FileDownload';

export default class Widget extends Component {

constructor(props) {

super(props);

this.state = widgetStore.getState().toJS();

}

handleDownload(data) {

widgetActions.downloadFile(data);

}

handleDownloadComplete() {

widgetActions.downloadFile();

}

render() {

const downloadPath = this.state.downloadPath;

return (

// button/icon with click bound to this.handleDownload goes here

{downloadPath &&

<FileDownload

actionPath={downloadPath}

onDownloadComplete={this.handleDownloadComplete}

/>

}

);

}

widgetActions.js-仅部分代码

export function downloadFile(data) {

let downloadPath = null;

if (data) {

downloadPath = `${apiResource}/${data.fileName}`;

}

appDispatcher.dispatch({

actionType: actionTypes.DOWNLOAD_FILE,

downloadPath

});

}

widgetStore.js-仅部分代码

let store = Map({

downloadPath: null,

isLoading: false,

// other store properties

});

class WidgetStore extends Store {

constructor() {

super();

this.dispatchToken = appDispatcher.register(action => {

switch (action.actionType) {

case actionTypes.DOWNLOAD_FILE:

store = store.merge({

downloadPath: action.downloadPath,

isLoading: !!action.downloadPath

});

this.emitChange();

break;

FileDownload.js-

完整,功能齐全的代码,可供复制和粘贴

-带Babel 6.x的React 0.14.7 [“ es2015”,“ react”,“ stage-0”]

-表单必须display: none是“隐藏的”形式“ className是为了

import React, {Component, PropTypes} from 'react';

import ReactDOM from 'react-dom';

function getFormInputs() {

const {queryParams} = this.props;

if (queryParams === undefined) {

return null;

}

return Object.keys(queryParams).map((name, index) => {

return (

<input

key={index}

name={name}

type="hidden"

value={queryParams[name]}

/>

);

});

}

export default class FileDownload extends Component {

static propTypes = {

actionPath: PropTypes.string.isRequired,

method: PropTypes.string,

onDownloadComplete: PropTypes.func.isRequired,

queryParams: PropTypes.object

};

static defaultProps = {

method: 'GET'

};

componentDidMount() {

ReactDOM.findDOMNode(this).submit();

this.props.onDownloadComplete();

}

render() {

const {actionPath, method} = this.props;

return (

<form

action={actionPath}

className="hidden"

method={method}

>

{getFormInputs.call(this)}

</form>

);

}

}

以上是 如何将React响应下载为文件 的全部内容, 来源链接: utcz.com/qa/431491.html

回到顶部