最近做了一些附件上传下载的功能,附件都是上传第三方平台,例如青云。该章记录一下附件下载。
有一些浏览器在打开第三方附件下载地址的时候,会直接打开文件,看到的都是二进制乱码,而不是弹出下载框,例如火狐浏览器,该实现方式是为了兼容这种情况。
第一步:前端把第三方下载地址传递给服务端,或者服务端自己从业务数据库直接获取。
第二步:前端把附件的名称传递给服务端,或者服务端自己从业务数据库直接获取,或自动生成。
第三步:通过http请求的方式获取附件的二进制流。
第四步:通过HttpServletResponse输出,浏览器会自动匹配,直接弹出下载框。
备注:文章结尾有源码下载地址,下载之后,可直接用eclipse导入maven项目运行。
一)DownloadController对外接口类代码
package com.oysept.attachment.controller;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.oysept.attachment.utils.DownloadUtils;
import com.oysept.attachment.utils.HttpUtils;
/**
* 附件下载controller
* @author ouyangjun
* 由于下载时返回一个流给前端,所以不能使用@RestController注解
*/
@Controller
@RequestMapping(value = "/attachment")
public class DownloadController {
/**
* 首页测试接口
* 地址: http://localhost:8080/attachment/download/home
* @return
*/
@RequestMapping(value = "/download/home", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
@ResponseBody
public String home(){
return "oysept attachment download home!";
}
/**
* 通过第三方下载地址,获取流返回给前端
* 原因: 有一些浏览器,直接输入第三方下载地址,会直接打开文件,不会直接下载,需要手动在浏览器配置之后才ok
* 注意: fileUrl和fileName需要urlencode
*/
@RequestMapping(value = "/download/urlDownload", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
public void urlDownload(@RequestParam(value="fileUrl") String fileUrl,
@RequestParam(value="fileName") String fileName, HttpServletResponse response) {
if (StringUtils.isEmpty(fileUrl) || StringUtils.isEmpty(fileName)) {
System.out.println("fileUrl or fileName is null!");
return;
}
try {
// 获取文件类型
String fileType = fileName.substring(fileName.lastIndexOf(".")+1,fileName.length())
.toLowerCase();
// 解码
String decodeFileUrl = URLDecoder.decode(fileUrl, "UTF-8");
System.out.println("decodeFileUrl: " + decodeFileUrl);
// 设置附件信息,fileName中如果包含中文,可能会出现乱码
response.setHeader("Content-disposition", "attachment; filename=" + URLDecoder.decode(fileName, "UTF-8"));
// ContentType,如果不设置,附件会打不开
String contentType = DownloadUtils.getContentType(URLDecoder.decode(decodeFileUrl, "UTF-8"));
System.out.println("/download/urlDownload ContentType: " + contentType);
if (StringUtils.isEmpty(contentType)) {
contentType = DownloadUtils.getResponseContentType(fileType);
// 设置HttpServletResponse设置的ContentType参数
if (!StringUtils.isEmpty(contentType)) {
response.setContentType(contentType);
}
} else {
response.setContentType(contentType + ";charset=utf-8");
}
// InputStream in = HttpUtils.getHttpInputStream(decodeFileUrl);
InputStream in = HttpUtils.getHttpInputStream(decodeFileUrl, "GET");
OutputStream out = null;
try {
out = response.getOutputStream();
int l = -1;
byte[] tmp =new byte[1024];
while ((l = in.read(tmp)) != -1) {
out.write(tmp, 0, l);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭低层流。
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
二)http工具类HttpUtils类代码
注意:该工具类有两种请求方式
说明:只要服务端能把附件读取成流的方式,就能通过HttpServletResponse输出的,可自行琢磨。
package com.oysept.attachment.utils;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
public class HttpUtils {
/**
* http获取InputStream, 方式一
* @param url
* @return
*/
public static InputStream getHttpInputStream(String requestUrl) {
try {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet =new HttpGet(requestUrl);
CloseableHttpResponse response = httpClient.execute(httpGet);
// 判断请求是否成功
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
HttpEntity entity = response.getEntity();
// 返回InputStream
return entity.getContent();
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* http获取InputStream, 方式二
* @param requestUrl: 请求路径
* @param requestMethod: 请求方式(GET、POST)
* @return
*/
public static InputStream getHttpInputStream(String requestUrl, String requestMethod) {
HttpURLConnection con = null;
InputStream is = null;
try {
URL url = new URL(requestUrl);
// 原生访问http请求,未代理请求
con = (HttpURLConnection) url.openConnection();
con.setDoOutput(true);
con.setDoInput(true);
con.setUseCaches(false);
con.setRequestMethod(requestMethod);
con.setReadTimeout(60000);
con.setConnectTimeout(60000);
// 获取InputStream
is = con.getInputStream();
return is;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
二)DownloadUtils工具类代码,主要是获取附件ContentType
package com.oysept.attachment.utils;
import java.io.File;
import javax.activation.MimetypesFileTypeMap;
import org.springframework.util.StringUtils;
/**
*
* @Author: ouyangjun
*/
public class DownloadUtils {
/**
* 获取HttpServletResponse设置的ContentType参数
* @return
*/
public static String getResponseContentType(String type) {
String contentType = null;
if ("xlsx,xls,docx,doc,pptx,ppt,dotx,docm,dotm,xltx,xlsm,xltm,xlam,xlsb,potx,ppsx,ppam,pptm,potm,ppsm,xlt,xla,pot,pps,ppa"
.contains(type)) {
// 附件header处理
if("xlsx".equals(type)) {
contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
} else if("xls".equals(type) || "xlt".equals(type) || "xla".equals(type)) {
contentType = "application/vnd.ms-excel;charset=utf-8";
} else if("docx".equals(type)) {
contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=utf-8";
} else if("doc".equals(type) || "dot".equals(type)) {
contentType = "application/msword;charset=utf-8";
} else if("pptx".equals(type)) {
contentType = "application/vnd.openxmlformats-officedocument.presentationml.presentation;charset=utf-8";
} else if("ppt".equals(type) || "pot".equals(type) || "pps".equals(type) || "ppa".equals(type)) {
contentType = "application/vnd.ms-powerpoint;charset=utf-8";
} else if("dotx".equals(type)) {
contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.template;charset=utf-8";
} else if("docm".equals(type)) {
contentType = "application/vnd.ms-word.document.macroEnabled.12;charset=utf-8";
} else if("dotm".equals(type)) {
contentType = "application/vnd.ms-word.template.macroEnabled.12;charset=utf-8";
} else if("xltx".equals(type)) {
contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.template;charset=utf-8";
} else if("xlsm".equals(type)) {
contentType = "application/vnd.ms-excel.sheet.macroEnabled.12;charset=utf-8";
} else if("xltm".equals(type)) {
contentType = "application/vnd.ms-excel.template.macroEnabled.12;charset=utf-8";
} else if("xlam".equals(type)) {
contentType = "application/vnd.ms-excel.addin.macroEnabled.12;charset=utf-8";
} else if("xlsb".equals(type)) {
contentType = "application/vnd.ms-excel.sheet.binary.macroEnabled.12;charset=utf-8";
} else if("potx".equals(type)) {
contentType = "application/vnd.openxmlformats-officedocument.presentationml.template;charset=utf-8";
} else if("ppsx".equals(type)) {
contentType = "application/vnd.openxmlformats-officedocument.presentationml.slideshow;charset=utf-8";
} else if("ppam".equals(type)) {
contentType = "application/vnd.ms-powerpoint.addin.macroEnabled.12;charset=utf-8";
} else if("pptm".equals(type)) {
contentType = "application/vnd.ms-powerpoint.presentation.macroEnabled.12;charset=utf-8";
} else if("potm".equals(type)) {
contentType = "application/vnd.ms-powerpoint.template.macroEnabled.12;charset=utf-8";
} else if("ppsm".equals(type)) {
contentType = "application/vnd.ms-powerpoint.slideshow.macroEnabled.12;charset=utf-8";
}
} else if ("txt".equals(type)) {
contentType = "text/plain;charset=utf-8";
} else if ("pdf".equals(type)) {
contentType = "application/pdf;charset=utf-8";
} else if ("png".equals(type)) {
contentType = "image/png;charset=utf-8";
} else if ("jpg".equals(type) || "jpeg".equals(type)) {
contentType = "image/jpeg;charset=utf-8";
} else if ("bmp".equals(type)) {
contentType = "application/x-bmp;charset=utf-8";
} else if ("gif".equals(type)) {
contentType = "image/gif;charset=utf-8";
} else if ("html".equals(type) || "htm".equals(type)) {
contentType = "text/html;charset=utf-8";
} else if ("xml".equals(type)) {
contentType = "text/xml;charset=utf-8";
} else if ("css".equals(type)) {
contentType = "text/css;charset=utf-8";
} else if ("js".equals(type)) {
contentType = "application/x-javascript;charset=utf-8";
} else if ("eml".equals(type)) {
contentType = "message/rfc822;charset=utf-8";
} else if ("xlw".equals(type)) {
contentType = "application/x-xlw;charset=utf-8";
} else if ("ai".equals(type)) {
contentType = "application/postscript;charset=utf-8";
} else if ("rtf".equals(type)) {
contentType = "application/msword;charset=utf-8";
} else if ("mp4".equals(type)) {
contentType = "video/mpeg4;charset=utf-8";
} else if ("tif".equals(type) || "tiff".equals(type)) {
contentType = "image/tiff;charset=utf-8";
} else if ("exe".equals(type)) {
contentType = "application/x-msdownload;charset=utf-8";
} else if ("pls".equals(type)) {
contentType = "audio/scpls;charset=utf-8";
} else if ("rpm".equals(type)) {
contentType = "audio/x-pn-realaudio-plugin;charset=utf-8";
} else if ("mht".equals(type)) {
contentType = "message/rfc822;charset=utf-8";
}
return contentType;
}
/**
* 获取附件 ContentType
* @param url
* @return
*/
public static String getContentType(String url) {
if (StringUtils.isEmpty(url)) {
return null;
}
try {
return new MimetypesFileTypeMap().getContentType(new File(url));
} catch (Exception e) {
return null;
}
}
}
识别二维码关注个人微信公众号
本章完结,待续,欢迎转载!
本文说明:该文章属于原创,如需转载,请标明文章转载来源!