pdf 从后端接口获取的是instream 输入流,想要在前端下载,需要写到输入流
ServletOutputStream 中 该类继承了OutputStream
public abstract class ServletOutputStream extends OutputStream {
private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.LocalStrings");
protected ServletOutputStream() {
}
放到
HttpServletResponse 返回给前端 ,上完整代码
public static void sendPostDownLoadPdf(HttpServletResponse response, String url) throws IOException {
// 创建默认的httpClient实例.
CloseableHttpClient httpclient = HttpClients.createDefault();
// 创建httpGet
HttpGet httpGet = new HttpGet(url);
try {
// 设置请求头参数
CloseableHttpResponse httpResponse = httpclient.execute(httpGet);
// 获取第三方接口放在Response中的文件名
org.apache.http.Header[] headers = httpResponse.getHeaders("content-disposition");
// 字符集编码
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setContentType("application/octet-stream");
if (headers != null && headers.length > 0) {
String contentDispositionValue = headers[0].getValue();
// 设置文件名,这里没用加密文件名,可以自己设置
response.addHeader("Content-Disposition", URLEncoder.encode(contentDispositionValue,"UTF-8"));
}
HttpEntity entity = httpResponse.getEntity();
//获取输入流
InputStream inputStream = entity.getContent();
// 输出文件
ServletOutputStream outputStream = response.getOutputStream();
try {
byte[] oBuff = new byte[1024];
int iSize;
while (-1 != (iSize = inputStream.read(oBuff))) {
outputStream.write(oBuff, 0, iSize);
}
outputStream.flush();
} finally {
IOUtils.close(outputStream);
IOUtils.close(inputStream);
}
outputStream.close();
} catch (Exception e){
e.printStackTrace();
} finally {
try {
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
前端实现 :下载
.then((res) => {
if (res) {
let temp = res.headers["content-disposition"]
.split(";")[1]
.split("=")[1];
//对文件名乱码转义--【Node.js】使用iconv-lite解决中文乱码
let iconv = require("iconv-lite");
let fileName = iconv.decode(temp, "gbk");
let type = decodeURIComponent(res.headers["content-type"]); // const xlsx = 'application/vnd.ms-excel'
// console.log(type);
const blob = new Blob([res.data], {
type: "application/vnd.ms-excel;charset=UTF-8",
});
// console.log(blob);
const blobUrl = window.URL.createObjectURL(blob);
let a = document.createElement("a"); // 转换完成,创建一个a标签用于下载
a .style.display = 'none'
// 设置href属性为文件路径,download属性可以设置文件名称
a.href = blobUrl;
a.download = fileName;
document.body.appendChild(a);
a.click();
Window.URL.revokeObjectURL(blobUrl);
a.remove();
} else {
this.$message.error("导出失败");
}
});
后端展示
这里需要介绍我们项目的特殊性, 首先 我们是移动端h5 ,并且展示pdf 的方不能有多余的框框,
vue 展示 pdf 不能用 img 标签 因为不支持, 使用
1,<iframe src="${pageContext.request.contextPath}/admin/indexs/images/resume.pdf" width="800px" height="2400px"></iframe>
可以,由于我们项目原因不允许使用。。。。。。
2,vue-pdf 可以,但是他预览之后有多余的框框,我们设计说了不行,不好看,也不知道什么审美。。。
3,杀手锏
后端将pdf 转 img ... 这个相对来说比较麻烦
首先了解这个类
ImageIO jdk 8 的提供里这样的方法
public static boolean write(RenderedImage im, String formatName, OutputStream output) throws IOException { }有兴趣的同学可以看源码 就是 将inputStream 装 byte[] 然后传入 outPutstrem 流,和后缀文件名png/jpg;;等
直接上代码
public static void sendPostViewLoadPdf(HttpServletResponse response, String url) throws IOException {
// 创建默认的httpClient实例.
CloseableHttpClient httpclient = HttpClients.createDefault();
// 创建httpGet
HttpGet httpGet = new HttpGet(url);
try {
// 设置请求头参数
CloseableHttpResponse httpResponse = httpclient.execute(httpGet);
// 字符集编码
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setContentType("image/png");
response.addHeader("Content-Disposition", URLEncoder.encode("电子发票.png","UTF-8"));
HttpEntity entity = httpResponse.getEntity();
//获取输入流
InputStream inputStream = entity.getContent();
// 输出文件
ServletOutputStream outputStream = response.getOutputStream();
try {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = -1;
while((len = inputStream.read(buffer)) != -1){
outStream.write(buffer, 0, len);
}
outStream.close();
PdfUtils.pdf2Image(outStream.toByteArray(), outputStream, "PNG");
outputStream.flush();
} finally {
IOUtils.close(outputStream);
IOUtils.close(inputStream);
}
outputStream.close();
} catch (Exception e){
e.printStackTrace();
} finally {
try {
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 这里是pdf转图片的代码,先要对pdf 长宽处理。。。
public static boolean pdf2Image(final byte[] content, final OutputStream output, final String ext) {
if (null == content ) {
return false;
}
int width = 0;
int[] singleImgRGB;
int shiftHeight = 0;
try (PDDocument pdDocument = PDDocument.load(content)) {
PDFRenderer renderer = new PDFRenderer(pdDocument);
BufferedImage imageResult = null;
for (int i = 0, len = pdDocument.getNumberOfPages(); i < len; i++) {
BufferedImage image = renderer.renderImageWithDPI(i, 128, ImageType.RGB);
int imageHeight = image.getHeight();
int imageWidth = image.getWidth();
//计算高度和偏移量
if (i == 0) {
//使用第一张图片宽度;
width = imageWidth;
//保存每页图片的像素值
imageResult = new BufferedImage(width, imageHeight * len, BufferedImage.TYPE_INT_RGB);
} else {
// 计算偏移高度
shiftHeight += imageHeight;
}
singleImgRGB = image.getRGB(0, 0, width, imageHeight, null, 0, width);
// 写入流中
imageResult.setRGB(0, shiftHeight, width, imageHeight, singleImgRGB, 0, width);
return ImageIO.write(imageResult, CheckUtils.isNotBlank(ext) ? ext.toUpperCase() : "PNG", output);
}
} catch (Exception e) {
}
return false;
}
前端就会接收到img 的流 他 在转 base64 就可以用 img 标签显示了。打完收工。。。。