在开发本功能时出现过以下问题:
1.Windows环境下转换出来的PDF文件不会出现乱码,而服务器上的文件出现了中文乱码,最后排查原因是因为linux服务器上没有中文字体,把Windows系统径为 "C:\Windows\Fonts" 下面的字体拷贝到linux系统的usr/shear/font目录下即可。注意:如果咱们的程序是部署在docker容器下,那么需要对宿主机的这个文件目录做数据挂载。
2.word或excel转PDF格式后,文件会变大,后台处理时等待时间会比较久,根据网上的资料,可以尝试升级aspose的版本,有方法可以让pdf文件数据压缩,使效率变快,但会影响到转换出来的文件质量。
3.在后端程序中使用到流的地方一定要在创建代码那结尾部分关闭流,否则会造成内存溢出。在Java中,有一些数据类型不会被垃圾回收器(GC)回收。数据类型包括:
- 静态变量:静态变量是属于类的变量,而不是对象的变量。它们在整个程序的生命周期中都存在,除非显式地清除它们或者程序终止。
- 对象的引用:如果一个对象有引用指向它,即使该对象无法通过其他引用访问,它也不会被垃圾回收。这包括在方法中创建的局部变量,如果它们被其他方法引用或赋值给类变量。
- Native方法中的对象:在Native方法中创建的对象属于本地方法堆栈,而不是Java堆栈。因此,这些对象不受Java堆的垃圾回收管理。
- 被Finalize()方法重写的对象:如果一个对象重写了Finalize()方法,垃圾回收器会将其放入一个特殊的待清理队列,并在适当的时候调用该方法。但并不是所有的对象都会被垃圾回收器调用Finalize()方法,因此不能依赖Finalize()方法来释放资源。 (虽然这些数据类型不会被自动回收,但它们可以在适当的时候通过手动的方式进行清理和释放。)
话不多说,下面上代码:
package com.springboot.cloud.mes.common.utils;
import com.aspose.cells.License;
import com.aspose.cells.PdfSaveOptions;
import com.aspose.cells.SaveFormat;
import com.aspose.cells.Workbook;
import com.aspose.words.Document;
import com.aspose.words.FontSettings;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import java.io.*;
/**
* PDF工具类
*/
@Slf4j
public class PdfUtils {
private static final String windowsFontsPath = "C:/Windows/Fonts/";
private static final String linuxFontsPath = "/usr/share/fonts/Chinese/";
/**
* EXCEL转PDF
* @param filePath pdf存放路径
* @param inputStream 源文件流
* @throws Exception
*/
public static void ExcelToPdf(String filePath,InputStream inputStream) throws Exception{
if (!getLicense()) {
// 验证License 若不验证则转化出的pdf文档会有水印产生
return;
}
File file=new File(filePath);
long start=System.currentTimeMillis();
Workbook wb=new Workbook(inputStream);
FontSettings.getDefaultInstance().setFontsFolders(new String[]{windowsFontsPath, linuxFontsPath}, true);
PdfSaveOptions pdfSaveOptions = new PdfSaveOptions();
//把内容放在一张PDF 页面上
pdfSaveOptions.setOnePagePerSheet(true);
FileOutputStream os=new FileOutputStream(file);
wb.save(os,pdfSaveOptions);
long end = System.currentTimeMillis();
log.info("excel转换耗时:" + (end - start)/1000.0 + "秒\n\n" + "文件保存在:" + file.getPath());
}
/**
* WORD转PDF
* @param filePath pdf存放路径
* @param inputStream 源文件流
* @throws Exception
*/
public static void WordToPdf(String filePath,InputStream inputStream) throws Exception{
File file=new File(filePath);
long start=System.currentTimeMillis();
Document dc=new Document(inputStream);
FileOutputStream os=new FileOutputStream(file);
FontSettings fs=new FontSettings();
fs.setFontsFolders(new String[]{windowsFontsPath,linuxFontsPath}, true);
dc.setFontSettings(fs);
dc.save(os,com.aspose.words.SaveFormat.PDF);
long end = System.currentTimeMillis();
log.info("word转换耗时:" + (end - start)/1000.0 + "秒\n\n" + "文件保存在:" + file.getPath());
}
private static boolean getLicense() {
boolean result = false;
try {
//license.xml路径
ClassPathResource resource = new ClassPathResource("license.xml");
InputStream is = resource.getInputStream();
License aposeLic = new License();
aposeLic.setLicense(is);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 输出PDF流
* @param is 生成的临时PDF文件流
* @param response HttpServletResponse
* @param contentType "application/pdf;charset=UTF-8"
* @param fileName 文件名称
*/
public static void writeFileToOutputStream(InputStream is, HttpServletResponse response,String contentType, String fileName) {
try {
response.setContentType(contentType);
response.setHeader("Content-Disposition", "inline; filename="+ URLEncoder.encode(fileName,"UTF-8"));
response.setCharacterEncoding("UTF-8");
OutputStream os = response.getOutputStream();
byte[] bytes = IOUtils.toByteArray(is);
os.write(bytes);
os.flush();
os.close();
is.close();
} catch (Exception e) {
log.error("文件预览失败");
e.printStackTrace();
}
}
}
前端使用blob接收文件流,以下是部分参考代码:
if (
data.fileSuffix == 'doc' ||
data.fileSuffix == 'docx' ||
data.fileSuffix == 'xlsx' ||
data.fileSuffix == 'xls'
) {
this.datatype = 'pdf'
let url =
baseUrlDocument + '/fileStorage/view/' + data.id + '#toolbar=0'
const x = new window.XMLHttpRequest()
let token = getCookie('Admin-Token')
x.open('GET', url, true)
x.setRequestHeader('Authorization', 'Bearer ' + token)
x.responseType = 'blob' // 选择返回格式为blob --- 一般后端返回的是看不懂的文件流 故需要转成blob
let loadingInstance = Loading.service({
lock: true,
text: 'Loading',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
x.onload = () => {
let blob = new Blob([x.response], {
type: 'application/pdf;charset=utf-8'
})
let href = getObjectURL(blob)
this.viewUrl = href
this.changeType(type, data, x.status)
loadingInstance.close()
}
x.send()
} else {
this.$confirm(`该文件不支持预览`, '提示', {
confirmButtonText: '确定',
type: 'warning'
})
.then(async () => {})
.catch(() => {
this.$message({
type: 'info',
message: '已取消'
})
})
}