需求
需要将前端传来的多张图片合并成一个pdf文件。
Pdfbox方式实现
依赖
在pom.xml里导入pdfbox依赖, Apache PDFBox 库是一个用于处理 PDF 文档的开源 Java 工具。商用也是免费
<!-- pdf文件处理包导入 -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.24</version>
</dependency>
代码
/**
* @Description: 多张图片转化为pdf文件
* @author: yqk
* @date: 2024/5/16 20:47
* @param images: 前端传来的图片文件List
* @param savePdfName: 保存的pdf文件名
* @Return: com.mt.webtest.entity.ComResult
*/
@RequestMapping("/imageToPdf")
public ComResult imageToPdf(@RequestParam("images") List<MultipartFile> images, String savePdfName){
try{
//存放pdf文件的目录
String folderPath = "D:\\WorkData\\uploadTest";
File outFolder = new File(folderPath);
if(!outFolder.exists()){
//递归创建文件夹
outFolder.mkdirs();
}
if(images!=null || images.size()>0){
try(PDDocument document = new PDDocument()){//创建pdf文件对象
for (MultipartFile multipartImage : images) {
if (!multipartImage.isEmpty()) {
String fileName = multipartImage.getOriginalFilename();
String fileHouZui = getFileType(fileName);
if (!isImage(fileHouZui)) {
//传来的文件不是图片就跳过
continue;
}
PDImageXObject image = PDImageXObject.createFromByteArray(document, multipartImage.getBytes(), "image");
// pdf文件里添加新的一页,页的宽高按照图片的宽高设置
PDRectangle pageSize = new PDRectangle(image.getWidth(), image.getHeight());
PDPage page = new PDPage(pageSize);
document.addPage(page);
try( PDPageContentStream contentStream = new PDPageContentStream(document, page);){
// 计算图片在pdf页中的起始点(x,y)、宽度(scaledWidth)和高度(scaledHeight)。
float pageWidth = pageSize.getWidth();
float pageHeight = pageSize.getHeight();
float imageWidth = image.getWidth();
float imageHeight = image.getHeight();
float scale = Math.min(pageWidth / imageWidth, pageHeight / imageHeight);
float scaledWidth = imageWidth * scale;
float scaledHeight = imageHeight * scale;
float x = (pageWidth - scaledWidth) / 2;
float y = (pageHeight - scaledHeight) / 2;
//将图片填充入pdf页
contentStream.drawImage(image, x, y, scaledWidth, scaledHeight);
}catch (Exception e){
return ComResult.fail(e);
}
}
}
// 4. 保存PDF
document.save(new File(folderPath+File.separator+savePdfName+".pdf"));
// document.save(folderPath+File.separator+savePdfName+".pdf");
}catch (Exception e){
return ComResult.fail(e.toString());
}
}
return ComResult.success("文件上传成功",savePdfName);
} catch (Exception e){
return ComResult.fail("操作异常"+e.toString());
}
}
/**
* @Description: 提取后缀名
* @author: yqk
* @date: 2024/5/16 20:45
* @param name: 文件名
* @Return: java.lang.String
*/
private String getFileType(String name){
if(StrUtil.isEmpty(name)){
return null;
}
int index = 0;
if((index=name.lastIndexOf("."))>-1){
return name.substring(index+1).toLowerCase();
}
return null;
}
/**
* @Description: 判断后缀名是否属于文件类型
* @author: yqk
* @date: 2024/5/16 20:44
* @param name: 后缀名
* @Return: boolean false不是图片,true是图片
*/
private boolean isImage(String name){
if(StrUtil.isEmpty(name)){
return false;
}
return name.equals("jpg")
||name.equals("jpeg")
||name.equals("png")
||name.equals("gif")
||name.equals("bmp");
}
返回的实体类,可以不用此类直接返回字符串
package com.mt.webtest.entity;
import lombok.Data;
/**
* @author:yqk
* @create: 2024-05-15 15:51
* @Description:
*/
@Data
public class ComResult <T>{
private boolean isSuccess;
private int code;
private String msg;
private T data;
public ComResult(boolean isSuccess,int code,String msg,T data){
this.isSuccess = isSuccess;
this.code = code;
this.msg = msg;
this.data = data;
}
public static ComResult success(){
return success("操作成功",null);
}
public static <T> ComResult success(T data){
return success("操作成功",data);
}
public static ComResult success(String msg){
return success(msg,null);
}
public static <T> ComResult success(String msg ,T data){
return new ComResult(true,200,msg,data);
}
public static <T> ComResult fail(String msg ,T data){
return new ComResult(false,444,msg,data);
}
public static <T> ComResult fail(){
return fail("操作失败",null);
}
public static ComResult fail(String msg){
return fail(msg,null);
}
public static <T> ComResult fail(T data){
return fail("操作失败",data);
}
}
itextpdf方式实现
依赖
在pom.xml里导入itextpdf依赖,itextpdf的性能要比pdfbox好,但是商用是收费的,个人使用免费。
<!-- pdf文件处理包导入 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.4.3</version>
</dependency>
代码
/**
* @Description: 图片转pdf单线程版本
* @author: yqk
* @date: 2024/5/20 10:59
* @param data:
* @Return: byte[]
*/
public static byte[] multipartFileToPdf(List<MultipartFile> data,UploadFileMsg uploadFileMsg) {
Document document = new Document();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try{
PdfWriter.getInstance(document, bos);
document.open();
for (MultipartFile datum : data) {
Image image = null;
if (uploadFileMsg.getIsCompress()!= null&&uploadFileMsg.getIsCompress().equals("1")) {//压缩图片
try{
List<MultipartFile> multipartCompressFilesList = UploadUtil.compressToFile(UploadUtil.transferToFile(datum),"1","1",null,null);
image = Image.getInstance(multipartCompressFilesList.get(0).getBytes());
}catch (Exception e){
log.error("图片压缩失败:"+e);
}
}
if(image==null){//不压缩图片
image = Image.getInstance(datum.getBytes());//加载图片
}
float imageWidth = image.getWidth();
float imageHeight = image.getHeight();
// 计算图片在pdf中的缩放比例(scale),和页边距(x,y)。
Rectangle pageSize = null;
if(StrUtil.isNotEmpty(uploadFileMsg.getPageSize())){
switch (uploadFileMsg.getPageSize().toUpperCase()){
case "A0":pageSize = PageSize.A0;break;
case "A1":pageSize = PageSize.A1;break;
case "A2":pageSize = PageSize.A2;break;
case "A3":pageSize = PageSize.A3;break;
case "A4":pageSize = PageSize.A4;break;
case "A5":pageSize = PageSize.A5;break;
case "A6":pageSize = PageSize.A6;break;
case "A7":pageSize = PageSize.A7;break;
case "A8":pageSize = PageSize.A8;break;
case "A9":pageSize = PageSize.A9;break;
}
}
if(pageSize == null){
pageSize = new Rectangle(imageWidth,imageHeight);
}
float pageWidth = pageSize.getWidth();
float pageHeight = pageSize.getHeight();
float scale = Math.min(pageWidth / imageWidth, pageHeight / imageHeight);
float scaledWidth = imageWidth * scale;
float scaledHeight = imageHeight * scale;
float x = (pageWidth - scaledWidth) / 2;
float y = (pageHeight - scaledHeight) / 2;
image.scalePercent(scale*100);
//设置pdf页大小, 和newPage位置不能对调,否则生成烂图
document.setPageSize(pageSize);
document.setMargins(x,x,y,y);
document.newPage();
document.add(image);
}
}catch (Exception e) {
e.printStackTrace();
}finally {
document.close();
}
return bos.toByteArray();
}
保存为文件
@Override
public CommResult uploadImagesConverPdfByItext(List<MultipartFile> uploadFiles, UploadFileMsg uploadFileMsg,
HttpServletRequest request, ResourceManagerUploadVO resourceManagerUpload){
System.out.println("开始:"+System.currentTimeMillis());
if(uploadFiles==null || uploadFiles.size()==0){
return CommResult.fail(400, "未检测到上传的文件!", "");
}
String foldername = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"));
String username = null;
LoginUser loginUser = (LoginUser) request.getSession().getAttribute("login");
if(loginUser == null){//当session里面没有人员信息时使用前端上传过来的username
username = resourceManagerUpload.getUserName();
}else{
username = loginUser.getUserName();
}
try{
long startTime = System.currentTimeMillis();
System.out.println("pdf转化开始:"+startTime);
byte[] bytes = multipartFileToPdfDuoXianChen(uploadFiles,uploadFileMsg);
// byte[] bytes = multipartFileToPdf(uploadFiles,uploadFileMsg);
if(bytes==null || bytes.length==0){
return CommResult.fail(400, "图片转化为pdf失败!", "");
}
long endTime = System.currentTimeMillis();
System.out.println("pdf转化结束:"+endTime);
System.out.println("pdf转化时间:"+(endTime-startTime));
if(StrUtil.isEmpty(uploadFileMsg.getSaveName())){
String saveName = uploadFiles.get(0).getOriginalFilename();
uploadFileMsg.setSaveName(saveName.substring(0,saveName.lastIndexOf(".")));
}
uploadFileMsg.setSaveName(uploadFileMsg.getSaveName()+".pdf");
MultipartFile NewMultipartFileYB =new MockMultipartFile("file", uploadFileMsg.getSaveName(), "text/plain", bytes);
Map<String, Object> map = new HashMap();
System.out.println("pdf保存到服务器开始:"+System.currentTimeMillis());
String url = OSSUtils.uploadFileToOSSNotFileName(MTConstants.aliyun_bucketcommon, NewMultipartFileYB);
System.out.println("pdf保存到服务器完成:"+System.currentTimeMillis());
if(StrUtil.isEmpty(url)){
return CommResult.fail(400, "文件保存失败!", "");
}
map.put("url",url);//路径
map.put("name",uploadFileMsg.getSaveName());//文件名
map.put("userName",username);//提交人
long size = NewMultipartFileYB.getSize();//文件大小
map.put("size", size);
map.put("createTime",foldername);//提交时间
System.out.println("结束:"+System.currentTimeMillis());
return CommResult.success(200,"文件上传结果返回!",map);
}catch (Exception e){
log.error("文件合并异常"+e);
return CommResult.fail(400, "文件合并异常!", "");
}
}
图片压缩方法
//压缩图片文件
public static List<MultipartFile> compressToFile(File file, String scaleType,String scaleA, String scaleWidth, String scaleHeight) throws Exception {
List<MultipartFile> multipartFileList =new ArrayList<>();
try {
String uuid=UUID.randomUUID().toString().replace("-","");
String originalFilename = file.getName();
String path = file.getPath();
String name = file.getName();
File fileSL = new File(path.replace(name,uuid+originalFilename));
boolean pDImage =true;
if (pDImage) {
if (scaleType!=null&&scaleType.equals("1")) {
float scale = 1;
scale = Float.valueOf(scaleA);
if (scaleA == null) {
scale= 0.5f;
}
ImgUtil.scale(
file,
fileSL,
scale//缩放比例
);
}else {
int scaleWidthA = 0;
int scaleHeightA = 0;
try {
scaleWidthA = Integer.parseInt(scaleWidth);
scaleHeightA = Integer.parseInt(scaleHeight);
} catch (NumberFormatException e) {
throw new RuntimeException("图片压缩比例错误");
}
if (scaleWidthA !=0&&scaleHeightA!=0) {
ImgUtil.scale(
file,
fileSL,
scaleWidthA,//缩放宽
scaleHeightA,//缩放高
null
);
}
}
FileInputStream inputSL = null;
MultipartFile multipartFileSL = null;
inputSL = new FileInputStream(fileSL);
multipartFileSL =new MockMultipartFile("file", originalFilename, "text/plain", IOUtils.toByteArray(inputSL));
multipartFileList.add(multipartFileSL);
}
return multipartFileList;
} catch (RuntimeException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return multipartFileList;
}