Voici le code dans 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;
})
}
});
}
La réponse renvoyée est un fichier .xlsx
. Je veux que l'utilisateur puisse l'enregistrer en tant que fichier, mais rien ne se passe. Je suppose que le serveur renvoie le bon type de réponse car dans la console, il est indiqué
Content-Disposition:attachment; filename="report.xlsx"
Qu'est-ce qui me manque? Que dois-je faire dans le réducteur?
La technologie de navigateur ne prend actuellement pas en charge le téléchargement de fichier directement à partir d'une requête Ajax. La solution consiste à ajouter un formulaire masqué et à le soumettre en arrière-plan pour que le navigateur déclenche la boîte de dialogue Enregistrer.
J'utilise une implémentation standard de Flux, donc je ne suis pas sûr de ce que devrait être le code exact de Redux (Réducteur), mais le flux de travail que je viens de créer pour le téléchargement d'un fichier va comme ceci ...
FileDownload
. Tout ce composant ne fait que restituer une forme masquée puis, à l'intérieur de componentDidMount
, soumettez immédiatement la fiche et appelez-la, c'est onDownloadComplete
prop.Widget
, avec un bouton/une icône de téléchargement (plusieurs en fait ... un pour chaque élément d'un tableau). Widget
a l'action correspondante et stocke les fichiers. Widget
importations FileDownload
.Widget
a deux méthodes liées au téléchargement: handleDownload
et handleDownloadComplete
.Widget
store a une propriété appelée downloadPath
. Il est défini sur null
par défaut. Lorsque sa valeur est définie sur null
, aucun téléchargement de fichier n'est en cours et le composant Widget
ne rend pas le composant FileDownload
.Widget
appelle la méthode handleDownload
qui déclenche une action downloadFile
. L'action downloadFile
NE fait PAS une demande Ajax. Il envoie un événement DOWNLOAD_FILE
au magasin en lui envoyant le downloadPath
pour le fichier à télécharger. Le magasin enregistre le downloadPath
et émet un événement de modification.downloadPath
, Widget
rendra FileDownload
en passant par les accessoires nécessaires, notamment downloadPath
, ainsi que la méthode handleDownloadComplete
en tant que valeur pour onDownloadComplete
.FileDownload
est rendu et que le formulaire est soumis avec method="GET"
(POST devrait également fonctionner) et action={downloadPath}
, la réponse du serveur va maintenant déclencher la boîte de dialogue Enregistrer du navigateur pour le fichier de téléchargement cible (testée dans IE 9/10, dernière modification). Firefox et Chrome).onDownloadComplete
/handleDownloadComplete
est appelé. Cela déclenche une autre action qui distribue un événement DOWNLOAD_FILE
. Cependant, cette fois, downloadPath
est défini sur null
. Le magasin enregistre downloadPath
sous le nom null
et émet un événement de modification.downloadPath
, le composant FileDownload
n'est pas rendu en Widget
et le monde est un endroit heureux.Widget.js - code partiel seulement
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 - code partiel uniquement
export function downloadFile(data) {
let downloadPath = null;
if (data) {
downloadPath = `${apiResource}/${data.fileName}`;
}
appDispatcher.dispatch({
actionType: actionTypes.DOWNLOAD_FILE,
downloadPath
});
}
widgetStore.js - code partiel uniquement
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
- code complet, entièrement fonctionnel, prêt à être copié et collé
- Réagissez 0.14.7 avec Babel 6.x ["es2015", "réagit", "stage-0"]
La forme - doit être display: none
, ce à quoi sert le "caché" 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>
);
}
}
Vous pouvez utiliser ces deux bibliothèques pour télécharger des fichiers http://danml.com/download.htmlhttps://github.com/eligrey/FileSaver.js/#filesaverjs
exemple
// for FileSaver
import FileSaver from 'file-saver';
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.blob();
}).then(function(blob) {
FileSaver.saveAs(blob, 'nameFile.Zip');
})
}
});
// for download
let download = require('./download.min');
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.blob();
}).then(function(blob) {
download (blob);
})
}
});
J'ai fait face au même problème une fois aussi ..__ Je l'ai résolu en créant un lien vide avec une référence comme ceci:
linkRef = React.createRef();
render() {
return (
<a ref={this.linkRef}/>
);
}
et dans ma fonction de recherche, j'ai fait quelque chose comme ceci:
fetch(/*your params*/)
}).then(res => {
return res.blob();
}).then(blob => {
const href = window.URL.createObjectURL(blob);
const a = this.linkRef.current;
a.download = 'Lebenslauf.pdf';
a.href = href;
a.click();
a.href = '';
}).catch(err => console.error(err));
en gros, j’ai assigné l’URL des blobs (href) au lien, défini l’attribut de téléchargement et imposé un clic sur le lien . Pour autant que je sache, c’est l’idée "de base" de la réponse fournie par @Nate . Je ne sais pas si c'est une bonne idée de le faire de cette façon ... c'est ce que j'ai fait.