这是其中的代码 actions.js
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文件。我希望用户能够将其保存为文件,但是什么也没有发生。我认为服务器返回的响应类型正确,因为在控制台中它说
.xlsx
Content-Disposition:attachment; filename="report.xlsx"
我想念的是什么?我应该在减速器中做什么?
浏览器技术当前不支持直接从Ajax请求下载文件。解决方法是添加一个隐藏的表单并将其提交到幕后,以使浏览器触发“保存”对话框。
我正在运行标准的Flux实现,因此不确定确切的Redux(Reducer)代码应该是什么,但是我刚刚为文件下载创建的工作流是这样的…
FileDownload
componentDidMount
onDownloadComplete
Widget
handleDownload
handleDownloadComplete
downloadPath
null
downloadFile
DOWNLOAD_FILE
method="GET"
action={downloadPath}
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是为了
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> ); } }