Jak pobrać fetch response w react as file
Oto kod w 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;
})
}
});
}
Zwróconą odpowiedzią jest plik .xlsx
. Chcę, aby użytkownik mógł zapisać go jako plik, ale nic się nie dzieje. Zakładam, że serwer zwraca właściwy typ odpowiedzi, ponieważ w konsoli jest napisane
Content-Disposition:attachment; filename="report.xlsx"
Co mi umyka? Co należy zrobić w reduktorze? 2 answers
Technologia przeglądarki obecnie nie obsługuje pobierania pliku bezpośrednio z żądania Ajax. Obejście polega na dodaniu ukrytego formularza i przesłaniu go za kulisami, aby przeglądarka uruchomiła okno dialogowe zapisu.
Uruchamiam standardową implementację Flux, więc nie jestem pewien, jaki powinien być dokładny kod Redux (reduktor), ale workflow, który stworzyłem do pobrania pliku, wygląda tak...
- mam komponent Reactowy o nazwie
FileDownload
. Wszystko co robi ten komponent to Renderuj Ukryty formularz, a następnie, wewnątrzcomponentDidMount
, natychmiast Prześlij formularz i wywołaj goonDownloadComplete
prop. - mam inny komponent Reactowy, nazwiemy go
Widget
, z przyciskiem/ikoną pobierania (wiele właściwie... po jednym dla każdej pozycji w tabeli).Widget
posiada odpowiednią akcję i przechowuje pliki.Widget
IMPORTFileDownload
. -
Widget
ma dwie metody związane z pobieraniem:handleDownload
ihandleDownloadComplete
. -
Widget
sklep posiada właściwość o nazwiedownloadPath
. Domyślnie ustawiona jest nanull
. Gdy jego wartość jest ustawiona donull
nie trwa pobieranie pliku, a komponentWidget
nie renderuje komponentuFileDownload
. - kliknięcie przycisku / ikony w
Widget
wywołuje metodęhandleDownload
, która uruchamia akcjędownloadFile
. Akcja {[22] } nie wysyła żądania Ajax. Wysyła ZdarzenieDOWNLOAD_FILE
do sklepu wysyłając wraz z nimdownloadPath
do pobrania pliku. Sklep zapisujedownloadPath
i emituje Zdarzenie change. - ponieważ istnieje teraz
downloadPath
,Widget
będzie renderowaćFileDownload
przechodząc w niezbędne właściwości, w tymdownloadPath
oraz metodahandleDownloadComplete
jako wartość dlaonDownloadComplete
. - Kiedy
FileDownload
jest renderowany, a Formularz jest przesyłany zmethod="GET"
(POST też powinien działać) iaction={downloadPath}
, odpowiedź serwera uruchomi teraz okno dialogowe zapisywania docelowego pliku do pobrania (testowane w IE 9/10, najnowszych Firefoksach i Chrome). - bezpośrednio po złożeniu formularza,
onDownloadComplete
/handleDownloadComplete
nazywa się. To uruchamia kolejną akcję, która wywołuje zdarzenieDOWNLOAD_FILE
. Jednak tym razemdownloadPath
jest Ustaw nanull
. Sklep zapisujedownloadPath
jakonull
i emituje Zdarzenie change. - ponieważ nie ma już
downloadPath
komponentFileDownload
nie jest renderowany wWidget
i świat jest szczęśliwym miejscem.
Widget.js-tylko częściowy kod
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-tylko częściowy kod
export function downloadFile(data) {
let downloadPath = null;
if (data) {
downloadPath = `${apiResource}/${data.fileName}`;
}
appDispatcher.dispatch({
actionType: actionTypes.DOWNLOAD_FILE,
downloadPath
});
}
WidgetStore.js-tylko częściowy kod
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
- kompletny, w pełni funkcjonalny kod gotowy do kopiowania i wklejania
- React 0.14.7 with Babel 6.x ["es2015", "react", "stage-0"]
- forma musi być display: none
, czyli to, co "ukryte" className
jest dla
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>
);
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-31 09:46:30
Możesz użyć tych dwóch bibliotek do pobrania plików http://danml.com/download.html https://github.com/eligrey/FileSaver.js/#filesaverjs
Przykład
// 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);
})
}
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-16 06:38:43