background

Today, I adjusted the report download function of an old project. The files used to be stored locally on the server and can be downloaded directly from the local computer, but now I need to get the files from the FTP server and return them to the foreground.

In theory, a few tweaks to the code should do the trick, but in practice it’s a bit of a hole. In this article, we’ll sort out the process and considerations for downloading Java Web application files.

File Download Process

The basic principle is to write data directly to the response stream and set the response type to binary stream format:

  1. Set the response code;
  2. Set the response file type octet-stream.
  3. Set the response header field attachment name;
  4. Want a ServletResponse OutputStream to write data.

The corresponding codes for steps 2, 3 and 4 are:

Download operation source code

Common file download codes are:

	@ResponseBody
	@RequestMapping(value = "/download") public void download(HttpServletRequest request, HttpServletResponse response,String reportId) {// TODO indicates the fileName corresponding to the reportId report. String fileName ="XXX daily report file.xlsx"; / / set the response type and accessories head domain response. SetCharacterEncoding ("utf-8");
		response.setContentType("application/octet-stream");
		response.setHeader("content-disposition"."attachment; filename="+ fileName); InputStream InputStream = null; try { OutputStream output = response.getOutputStream(); // Check whether the file existsif(! isFileExist(fileName)){ logger.warn("File/directory {} does not exist", pathName);
				response.getWriter().println("Report file does not exist!");
				return; } inputStream = new FileInputStream(new File(pathName)); int len = -1; byte[] bytes = new byte[2048]; // Write binary data to the Response stream of Responsewhile((len = inputStream.read(bytes)) ! = -1) { output .write(bytes, 0, len); } output.flush(); } catch (Exception e) { logger.error("Failed to download file",e);
			try {
				response.getWriter().println("Failed to download file!");
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}finally{
			if(output ! = null){ try { output.close(); } catch (IOException e) { logger.error("File download closed output stream exception",e); }}if(inputStream ! = null){ try { inputStream.close(); } catch (IOException e) { logger.error("File download closed input stream exception",e); }}}}Copy the code

Press key, run the download operation, XLSX type report file is now in the download bar, check the network request response header is:

The response header field sets the location

The response type and header field information must be set before the write is written, otherwise the attachment is unreadable. Adjust the code order to write first and then set the response header:

If you look at the header of the network response, you can see that the header field set later does not take effect:

Apocalypse of Programming

Why does the download attachment display differently if the setting order is different?

After repeated verification for more than ten times, it is found that the header field setting of Response. setHeader is invalid after write operation. Presumably this is determined by the order in which the HTTP communication protocol package is assembled, since the HTTP response header field information is assembled before the Body.

Finally, summarize the main points of file download:

  1. The download path must be set in the background. You cannot directly receive the download path of the foreground. Otherwise, the download path is available../..Path of arbitrary file download risk; If you want to receive a pathfileNameParameter must be verifiedfileNameCan’t contain../And other special paths;
  2. You must pay attention to the sequence of response header field Settings and stream write operations. Write after the set header fields; otherwise, the attachment is unavailable.
  3. The same is true for downloading from FTPServletResponseOutputStreamObject toFTPClientretrieveFile(filename, outputStream)Download directly to the output stream.