Antecedentes
Hoy, ajusté la función de descarga de informes de un proyecto antiguo. El archivo original se almacenó localmente en el servidor. La descarga se puede obtener directamente desde la máquina local. Ahora tenemos que cambiarla para obtener el archivo del servidor FTP y devolverlo a la recepción.
En teoría, el código se puede ajustar ligeramente, pero en realidad es un pequeño pozo. Este artículo clasificará el proceso y los puntos de atención para descargar archivos de aplicaciones web Java.
Proceso de descarga de archivos
La descarga de archivos es una característica anticuada. El principio básico es escribir datos directamente en la secuencia de respuesta y establecer el tipo de respuesta en formato de secuencia binaria:
- Establecer el código de respuesta;
- Establezca el tipo de archivo de respuesta octet-stream;
- Establezca el nombre del archivo adjunto del campo de encabezado de respuesta;
- Quiero escribir datos en la secuencia OutputStream de ServletResponse.
Los códigos correspondientes para el segundo, tercer y cuarto pasos son:
Descargar el código fuente de la operación
Los códigos de descarga de archivos comunes son:
@ResponseBody
@RequestMapping(value = "/download")
public void download(HttpServletRequest request, HttpServletResponse response,String reportId) {
// TODO 根据 reportId 查询报表对应的文件名称
String fileName = "xxx日报表文件.xlsx";
//设置响应类型和附件头域
response.setCharacterEncoding("utf-8");
response.setContentType("application/octet-stream");
response.setHeader("content-disposition", "attachment;filename="+ fileName);
//读取报表内容写入响应流对象
InputStream inputStream = null;
try {
OutputStream output = response.getOutputStream();
//检查文件是否存在
if(!isFileExist(fileName)){
logger.warn("文件/目录 {} 不存在", pathName);
response.getWriter().println("报表文件不存在!");
return;
}
inputStream = new FileInputStream(new File(pathName));
int len = -1;
byte[] bytes = new byte[2048];
// 向 Response 的响应流写入二进制数据
while ((len = inputStream.read(bytes)) != -1) {
output .write(bytes, 0, len);
}
output.flush();
} catch (Exception e) {
logger.error("下载文件异常",e);
try {
response.getWriter().println("下载文件异常!");
} catch (IOException ex) {
ex.printStackTrace();
}
}finally{
if(output != null){
try {
output.close();
} catch (IOException e) {
logger.error("下载文件关闭输出流异常",e);
}
}
if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
logger.error("下载文件关闭输入流异常",e);
}
}
}
}
复制代码
敲重点
Después de ejecutar la operación de descarga, el archivo de informe de tipo xlsx se encuentra ahora en la barra de descarga. El encabezado de respuesta para ver la solicitud de red es:
Ubicación del ajuste del campo del encabezado de respuesta
campo de encabezado de respuesta ajustado y la información de tipo debe ser en write
antes de escribir , de lo contrario, es el apego ilegible. Ajuste la secuencia del código, escriba y configure el encabezado de respuesta:
Al observar el encabezado de la respuesta de la red, se puede ver que el campo del encabezado establecido después no ha tenido efecto:
Programando Apocalipsis
¿Por qué el orden de visualización de los archivos adjuntos de descarga es diferente cuando el orden de configuración es diferente?
Verify repitió una docena de veces, que se encuentran response.setHeader
en write
el tiempo después de la operación, sistema de cabecera no es válido. Se especula que esto está determinado por el orden de ensamblaje de los paquetes de protocolo de comunicación http 因为 http 响应头域信息是在 body 之前组装的
.
Finalmente, resuma los puntos principales de la descarga del archivo:
- Descargar ruta debe fijarse en el fondo, no podrá recibir la ruta de descarga de la recepción directa, de lo contrario existe
../..
ningún riesgo de descargar una ruta de archivo, y si el camino que se ha recibido confileName
los parámetros, debe comprobarfileName
que no contiene../
otra vía especial; - Debe prestar atención al orden de la configuración del campo del encabezado de respuesta y la operación de escritura de flujo
写设头域后写
; de lo contrario, el archivo adjunto no está disponible; - La misma descarga FTP de la
ServletResponse
delOutputStream
objeto pasadoFTPClient
esretrieveFile(filename, outputStream)
descargado directamente a la corriente de salida.