validar validacion manejo leer formularios formulario fichero ejemplos descargar contenido con carga automaticamente asincrona archivos archivo javascript reactjs flux reactjs-flux

javascript - validacion - Cómo descargar la respuesta de búsqueda en reaccionar como archivo



validacion de formularios con javascript ejemplos (2)

La tecnología del navegador actualmente no es compatible con la descarga de un archivo directamente desde una solicitud Ajax. La solución alternativa es agregar un formulario oculto y enviarlo entre bastidores para que el navegador active el cuadro de diálogo Guardar.

Estoy ejecutando una implementación estándar de Flux, así que no estoy seguro de cuál debería ser el código exacto de Redux (Reducer), pero el flujo de trabajo que acabo de crear para la descarga de un archivo es el siguiente ...

  1. Tengo un componente React llamado FileDownload . Todo lo que hace este componente es renderizar un formulario oculto y luego, dentro de componentDidMount , enviar inmediatamente el formulario y llamarlo a la onDownloadComplete prop.
  2. Tengo otro componente React, lo llamaremos Widget , con un botón / ícono de descarga (muchos en realidad ... uno para cada elemento en una tabla). Widget tiene acción correspondiente y almacenar archivos. Widget importa FileDownload .
  3. Widget tiene dos métodos relacionados con la descarga: handleDownload y handleDownloadComplete .
  4. Widget tienda de Widget tiene una propiedad llamada downloadPath . Está establecido en null por defecto. Cuando su valor se establece en null , no hay descarga de archivos en curso y el componente Widget no representa el componente FileDownload .
  5. Al hacer clic en el botón / icono en Widget llama al método handleDownload que desencadena una acción downloadFile . La acción downloadFile NO hace una solicitud Ajax. Envía un evento DOWNLOAD_FILE a la tienda que envía junto con él la downloadPath del archivo para descargar. La tienda guarda downloadPath y emite un evento de cambio.
  6. Dado que ahora hay un FileDownload downloadPath , Widget hará que FileDownload pase los elementos necesarios, incluidos downloadPath y el método handleDownloadComplete como el valor de onDownloadComplete .
  7. Cuando se procesa FileDownload y se envía el formulario con method="GET" (POST debería funcionar también) y action={downloadPath} , la respuesta del servidor activará el diálogo Save del navegador para el archivo de descarga de destino (probado en IE 9/10 , los últimos Firefox y Chrome).
  8. Inmediatamente después del envío del formulario, se llama a onDownloadComplete / handleDownloadComplete . Esto desencadena otra acción que distribuye un evento DOWNLOAD_FILE . Sin embargo, esta vez, downloadPath se establece en null . La tienda guarda downloadPath como null y emite un evento de cambio.
  9. Como ya no existe una downloadPath el componente FileDownload no se representa en Widget y el mundo es un lugar feliz.

Widget.js - solo código parcial

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 - solo código parcial

export function downloadFile(data) { let downloadPath = null; if (data) { downloadPath = `${apiResource}/${data.fileName}`; } appDispatcher.dispatch({ actionType: actionTypes.DOWNLOAD_FILE, downloadPath }); }

widgetStore.js - solo código parcial

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
- código completo, completamente funcional listo para copiar y pegar
- Reaccionar 0.14.7 con Babel 6.x ["es2015", "reaccionar", "etapa-0"]
- formulario debe display: none que es el nombre de className "oculto" para

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> ); } }

Aquí está el código en 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 respuesta devuelta es un archivo .xlsx . Quiero que el usuario pueda guardarlo como un archivo, pero no pasa nada. Supongo que el servidor está devolviendo el tipo correcto de respuesta porque en la consola dice

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

¿Qué me estoy perdiendo? ¿Qué debería hacer en el reductor?


Puede usar estas dos bibliotecas para descargar archivos http://danml.com/download.html https://github.com/eligrey/FileSaver.js/#filesaverjs

ejemplo

// 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); }) } });