Skip to content
标签
文件
字数
26719 字
阅读时间
153 分钟

实现文件上传、下载、转换功能

相关依赖

groovy

    //POI
    compile group: 'org.apache.poi', name: 'poi', version: '4.1.0'
    compile group: 'org.apache.poi', name: 'poi-ooxml', version: '4.1.0'
    compile group: 'org.apache.poi', name: 'poi-ooxml-schemas', version: '4.1.0'
    compile group: 'org.apache.poi', name: 'poi-scratchpad', version: '4.1.0'

    compile 'cn.aghost:fastdfs-client-java:1.29.0'
    compile 'ant:ant:1.7.0'
    compile group: 'com.aspose', name: 'aspose-words', version: '15.8.0'
    compile group: 'com.commnetsoft', name: 'aspose-pdf', version: '18.9'
    compile group: 'org.apache.pdfbox', name: 'pdfbox', version: '2.0.18'
    compile group: 'com.aliyun.oss',name: 'aliyun-sdk-oss', version: '3.10.2'
    compile group: 'net.sourceforge.jexcelapi', name: 'jxl', version: '2.6.12'

    //html转换PDF
    implementation 'com.itextpdf:itext7-core:7.1.9'
    implementation 'com.itextpdf:html2pdf:2.1.6'

    //html转Docx
    compile ('org.docx4j:docx4j:6.1.1'){
        exclude group: 'org.slf4j', module: 'slf4j-log4j12'
    }
    compile 'org.docx4j:docx4j-JAXB-Internal:8.1.4'
    compile 'org.docx4j:xhtmlrenderer:3.0.0'
    compile 'org.docx4j:docx4j-ImportXHTML:8.0.3'

上传下载

定义接口

java

import com.commnetsoft.commons.utils.FileUtils;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.commons.utils.UriUtil;
import com.commnetsoft.file.constant.FileSystemConstant;
import com.commnetsoft.file.model.FileChunk;
import com.commnetsoft.file.model.FileEntity;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

public interface IFileHandler {
    /**
     * 从路由中获取文件路径
     * @param uri 路由
     * @return java.lang.String
     * @author wuzm
     * @date 2020/7/13
     */
    default String getPath(String uri) throws UnsupportedEncodingException {
        if(StringUtils.isBlank(uri)){
            return null;
        }
        URI newUri = URI.create(uri);
        String path = newUri.getPath();
        return URLDecoder.decode(path, StandardCharsets.UTF_8.name());
    }

    default String getFilename(String uri, FileEntity fileEntity) throws UnsupportedEncodingException {
        if(StringUtils.isBlank(uri)){
            return null;
        }
        String filename;
        if(null != fileEntity){
            filename = fileEntity.getName();
        }else{
            filename = UriUtil.getParam(uri, "filename");
            if(StringUtils.isBlank(filename)){
                return filename;
            }else{
                filename = URLDecoder.decode(filename, StandardCharsets.UTF_8.name());
            }
        }
        return filename;
    }


    default String getFileContentType(FileEntity fileEntity,String fileName){
        if(null != fileEntity){
            if(StringUtils.isBlank(fileName)){
                fileName = fileEntity.getName();
            }
            if(StringUtils.isNotBlank(fileEntity.getMimetype())){
                return fileEntity.getMimetype();
            }
        }
        if(StringUtils.isNotBlank(fileName)){
            return FileUtils.getMimeType(fileName);
        }
        return "application/octet-stream";
    }

    default Boolean isChunkFile(FileEntity fileEntity, String url) {
        if (null != fileEntity) {
            return fileEntity.getChunk() == FileSystemConstant.TRUE_NUM ? true : false;
        }
        return StringUtils.contains(url, FileChunk.FILE_UPLOADID);
    }

    default String getChunkFileUploadid(FileEntity fileEntity, String url) {
        if (null != fileEntity && StringUtils.isNotBlank(fileEntity.getUploadid())) {
            return fileEntity.getUploadid();
        }
        if (StringUtils.contains(url, FileChunk.FILE_UPLOADID)) {
            return StringUtils.substring(StringUtils.substringAfter(url, FileChunk.FILE_UPLOADID + "="), 0, 32);
        }
        return null;
    }

}

工具类

java

import org.apache.commons.lang3.ArrayUtils;

import java.io.*;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

/**
 * 文件工具类
 * @author sunwen
 * @since 2019/12/27
 */
public class FileUtils extends org.apache.commons.io.FileUtils {

    /**
     * 创建文件夹
     * @author sunwen
     * @since 2020/1/7
     */
    public static File mkdirs(String path) throws IOException {
        File file = new File(path);
        if(file.isDirectory()){
            return file;
        }else if(file.mkdirs()){
            return file;
        }
        throw new IOException();
    }

    /**
     * 根据文件获取mimeType
     * @author sunwen
     * @since 2019/12/27
     */
    public static String getMimeType(File file){
        return getMimeType(file.getName());
    }

    /**
     * 根据文件获取mimeType
     * @author sunwen
     * @since 2019/12/27
     */
    public static String getMimeType(String fileName){
        FileNameMap fileNameMap = URLConnection.getFileNameMap();
        return fileNameMap.getContentTypeFor(fileName);
    }

    /**
     * 获取文件后缀
     * @param fileName 文件名
     * @author wuzm
     * @date 2020/7/1
     */
    public static String getSuffix(String fileName){
        if(StringUtils.isBlank(fileName)){
            return null;
        }
        return StringUtils.substringAfterLast(fileName,".");
    }


    /**
     * 拆分大文件
     * @param inputStream
     * @param size  拆分后每个小文件的大小(单位:MB)
     * @param fileName  原文件名称(带后缀)
     * @author lijx
     * @date 2020/12/11
     */
    public static List<File> splitFile(InputStream inputStream, int size, String fileName) throws IOException {
        if (size == 0 || null == inputStream) {
            return null;
        }
        //获取文件名和文件后缀
        String suffix = "";
        if (StringUtils.isNotBlank(fileName)) {
            String[] split = StringUtils.split(fileName, ".");
            if (ArrayUtils.isNotEmpty(split) && split.length == 2) {
                fileName = split[0];
                suffix = "." + split[1];
            }
        } else {
            fileName = UUIDUtils.generate();
        }

        File tempDirPath = new File(System.getProperty("java.io.tmpdir"));
        List<File> files = new ArrayList<>();

        FileOutputStream fos = null;
        byte[] buffer = new byte[1024 * 1024];
        int length = 0;
        int total = 0;//每次写入1MB,每个文件写入的次数
        int index = 0;
        try {
            File newFile = new File(tempDirPath, fileName + "_" + index + suffix);
            fos = new FileOutputStream(newFile);
            while ((length = inputStream.read(buffer)) != -1) {//不能一次性读完,大文件会内存溢出
                fos.write(buffer, 0, length);
                total++;
                if (total != size) {
                    continue;
                }
                index++;
                total = 0;
                files.add(newFile);
                newFile = new File(tempDirPath, fileName + "_" + index + suffix);
                fos = new FileOutputStream(newFile);
            }
            if (total != 0) {
                files.add(newFile);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != fos) fos.close();
        }
        return files;
    }


    /**
     * 拆分大文件
     * @param file
     * @param size  拆分后每个小文件的大小(单位:MB)
     * @author lijx
     * @date 2020/12/11
     */
    public static List<File> splitFile(File file, int size) throws IOException {
        if (null == file) {
            return null;
        }
        FileInputStream fileInputStream = null;
        List<File> files = null;
        try {
            fileInputStream = new FileInputStream(file);
            files = splitFile(fileInputStream, size, file.getName());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != fileInputStream) fileInputStream.close();
        }
        return files;
    }

}
java

import org.apache.commons.collections.CollectionUtils;

import java.util.Collection;
import java.util.List;

/**
 * 字符串工具类
 */
public class StringUtils extends org.apache.commons.lang3.StringUtils {

    public static String defaultString(Object obj, String defaultStr){
        return obj == null ? defaultStr : obj.toString();
    }

    /**
     * 判断字符是否是纯字母
     * @author sunwen
     * @since 2019/9/11
     */
    public static boolean isLetter(final CharSequence cs){
        if (isEmpty(cs)) {
            return false;
        }
        final int sz = cs.length();
        for (int i = 0; i < sz; i++) {
            char c = cs.charAt(i);
            if (!(c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z')){
                return false;
            }
        }
        return true;
    }

    /**
     * 判断字符是否是纯字母+数字
     * @author sunwen
     * @since 2019/9/11
     */
    public static boolean isLetternumeric(final CharSequence cs){
        if (isEmpty(cs)) {
            return false;
        }
        final int sz = cs.length();
        for (int i = 0; i < sz; i++) {
            char c = cs.charAt(i);
            if (!(c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c >= '0' && c <= '9')){
                return false;
            }
        }
        return true;
    }

    /**
     * 判断字符串是否一个hex数字
     * @author sunwen
     * @since 2019/8/7
     */
    public static boolean isHexNumeric(final CharSequence cs) {
        if (isEmpty(cs)) {
            return false;
        }
        final int sz = cs.length();
        for (int i = 0; i < sz; i++) {
            char c = cs.charAt(i);
            if (!Character.isDigit(c) && !(c >= 'A' && c <= 'F') && !(c >= 'a' && c <= 'f')) {
                return false;
            }
        }
        return true;
    }

    /**
     * 判断字符串是否符合java标识符名称
     * @author sunwen
     * @since 2019/4/2
     */
    public static boolean isJavaIdentifier(final CharSequence cs) {
        if (isEmpty(cs)) {
            return false;
        }
        final int sz = cs.length();
        for (int i = 0; i < sz; i++) {
            if (i == 0) {
                if (!Character.isJavaIdentifierStart(cs.charAt(i))) {
                    return false;
                }
            } else {
                if (!Character.isJavaIdentifierPart(cs.charAt(i))) {
                    return false;
                }
            }
        }
        return true;
    }

    public static boolean isJavaIdentifierPart(final CharSequence cs) {
        if (isEmpty(cs)) {
            return false;
        }
        final int sz = cs.length();
        for (int i = 0; i < sz; i++) {
            if (!Character.isJavaIdentifierPart(cs.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    /**
     * 判断字符串是否复合java包名
     * @author sunwen
     * @since 2019/6/11
     */
    public static boolean isJavaPackage(final String cs) {
        if (isEmpty(cs)) {
            return false;
        }
        if (startsWith(cs, "\\.") || endsWith(cs, "\\.")) {
            return false;
        }
        String[] parts = split(cs, "\\.");
        for (String str : parts) {
            if (!isJavaIdentifier(str)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 数字分割
     * @author sunwen
     * @since 2019/5/10
     */
    public static Integer[] splitInt(String str, String separatorChars) {
        String[] parts = split(str, separatorChars);
        Integer[] ints = new Integer[parts.length];
        for (int i = 0; i < parts.length; i++) {
            ints[i] = Integer.parseInt(parts[i]);
        }
        return ints;
    }


    public static String byteToHex(byte[] bytes){
        StringBuilder builder = new StringBuilder();
        if (bytes == null || bytes.length <= 0) {
            return null;
        }
        for (int i = 0; i < bytes.length; i++) {
            int v = bytes[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                builder.append(0);
            }
            builder.append(hv);
        }
        return builder.toString();
    }

    /**
     * 将集合用指定字符进行合并成字符串
     * @param collection 集合
     * @param separatorChars 指定字符
     * @author wuzm
     * @date 2020/5/17
     */
    public static String castToString(Collection collection ,String separatorChars){
        if(CollectionUtils.isEmpty(collection)){
            return "";
        }
        StringBuilder stringBuilder = new StringBuilder();
        for (Object collectEle : collection) {
            if(stringBuilder.length() > 0){
                stringBuilder.append(separatorChars);
            }
            stringBuilder.append(collectEle);
        }
        return stringBuilder.toString();
    }
}
java

import org.apache.http.client.utils.URIUtils;
import org.springframework.lang.Nullable;

import java.net.URI;
import java.util.Objects;

/**
 * URI工具类
 *
 * @author Brack.zhu
 * @date 2019/7/31
 */
public class UriUtil {
    
    //FileChunk.FILE_UPLOADID=“uploadid”;

    private UriUtil() {
    }

    /**
     * 获取最后一个path<br/>
     * http://127.0.0.1:8080/styles/font/fontawesome-webfont.woff?v=4.7.0<br/>
     * 返回 fontawesome-webfont.woff
     *
     * @param uri
     * @return 无返回null
     */
    public static String lastPath(URI uri) {
        String[] paths = getPaths(uri);
        if (null != paths && paths.length > 0) {
            return paths[paths.length - 1];
        }
        return null;
    }

    /**
     * 获取URI path集合<br/>
     * http://127.0.0.1:8080/styles/font/fontawesome-webfont.woff?v=4.7.0<br/>
     * 返回 [styles,font,fontawesome-webfont.woff]
     *
     * @param uri
     * @return 无返回null
     */
    public static String[] getPaths(URI uri) {
        Objects.requireNonNull(uri);
        String path = uri.getPath();
        if (StringUtils.isNotEmpty(path)) {
            return path.split("/");
        }
        return null;
    }

    /**
     * URI路径提取文件扩展名
     * @param path 路径地址(如:"/products/index.html")
     * @return 文件扩展名 (如:"html")
     */
    @Nullable
    public static String getFileExtension(String path) {
        int end = path.indexOf('?');
        int fragmentIndex = path.indexOf('#');
        if (fragmentIndex != -1 && (end == -1 || fragmentIndex < end)) {
            end = fragmentIndex;
        }
        if (end == -1) {
            end = path.length();
        }
        int begin = path.lastIndexOf('/', end) + 1;
        int paramIndex = path.indexOf(';', begin);
        end = (paramIndex != -1 && paramIndex < end ? paramIndex : end);
        int extIndex = path.lastIndexOf('.', end);
        if (extIndex != -1 && extIndex > begin) {
            return path.substring(extIndex + 1, end);
        }
        return null;
    }


    /**
     * Get the query of an URI.
     *
     * @param uri a string regarded an URI
     * @return the query string; <code>null</code> if empty or undefined
     */
    public static String getQuery(String uri) {
        if (uri == null || uri.length() == 0) { return null; }
        // consider of net_path
        int at = uri.indexOf("//");
        int from = uri.indexOf(
                "/",
                at >= 0 ? (uri.lastIndexOf("/", at - 1) >= 0 ? 0 : at + 2) : 0
        );
        // the authority part of URI ignored
        int to = uri.length();
        // reuse the at and from variables to consider the query
        at = uri.indexOf("?", from);
        if (at >= 0) {
            from = at + 1;
        } else {
            return null;
        }
        // check the fragment
        if (uri.lastIndexOf("#") > from) {
            to = uri.lastIndexOf("#");
        }
        // get the path and query.
        return (from < 0 || from == to) ? null : uri.substring(from, to);
    }

    /**
     * 获取路径中的指定参数值
     * @author wuzm
     * Date 2020/1/9 21:08
     */
    public static String getParam(String uri,String paramName){
        if(StringUtils.isBlank(paramName)){
            return uri;
        }

        String params = getQuery(uri);
        if(StringUtils.isBlank(params)){
            return null;
        }

        String[] paramsArr = StringUtils.split(params,"&");
        if(null == paramsArr && paramsArr.length == 0){
            return null;
        }

        String prefix = paramName + "=";
        for (String paramMap : paramsArr) {
            if (!StringUtils.startsWith(paramMap, prefix)) {
                continue;
            }
            return StringUtils.substringAfter(paramMap, prefix);
        }
        return null;
    }

    /**
     * 截取掉路由后最后的斜杠
     * @author wuzm
     * Date 2020/3/31 14:18
     */
    public static String subUriLastSlash(String uri){
        if(StringUtils.isBlank(uri)){
            return uri;
        }
        return StringUtils.endsWith(uri, "/") ? uri.substring(0,uri.length()-1) : uri;
    }

}

实体类

java

import tk.mybatis.mapper.annotation.LogicDelete;

import javax.persistence.*;
import java.util.Date;

/**
* @className: FileEntity
* @author: lijx
* @date: 2020/7/1 11:12
* @version: 1.0
*/
@Table(name = "file_entity")
public class FileEntity implements AclEntity<Integer> {

    public transient static final Integer PREFIXTYPE_THIS = 1;//搜索本级
    public transient static final Integer PREFIXTYPE_SUB = 2;//搜索下级
    public transient static final Integer PREFIXTYPE_CONTAIN = 3;//搜索包含


    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    //原文件id
    private String sourcefileids;
    //所属文件系统名称空间
    private String namespace;
    //所属文件空间主键
    private Integer spaceid;
    //上传唯一标识
    private String uploadid;
    //文件名称
    private String name;
    //文件类型
    private String mimetype;
    //文件前缀
    private String prefix;
    //文件后缀
    private String suffix;
    //文件大小
    private Long size;
    //文件路径
    private String url;
    //过期时间
    private Date expiretime;
    //是否分片
    private Integer chunk;
    //分片数量
    private Integer totalchunk;
    //是否完成
    private Integer completed;
    //文件md5加密值
    private String encryptvalue;
    //文件分片后md5值集合的md5结果
    private String chunkencryptvalue;
    //访问策略: 1.公开读写  2.公开读-私有写 3.私有读写 4.默认规则
    private Integer accesstype;
    //拥有者,可用于查询
    private String owner;
    @OrderBy
    //排序号
    private Integer orderby;
    //下载次数
    private Integer downloadnum;
    //创建时间
    private Date createtime;
    //修改时间
    private Date updatetime;
    //删除标识
    private Integer filedeleted;
    @LogicDelete
    private Boolean deleted;
}
java

import com.commnetsoft.db.model.IdEntity;
import tk.mybatis.mapper.annotation.LogicDelete;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * 文件分片表
* @className: FileChunk
* @author: lijx
* @date: 2020/7/1 11:12
* @version: 1.0
*/
@Table(name = "file_chunk")
public class FileChunk implements IdEntity<Integer> {

    public transient static final String FILE_UPLOADID = "uploadid";

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    //文件id
    private Integer fileid;
    //分片名称
    private String name;
    //文件路径
    private String url;
    //文件大小
    private long size;
    //分片序号
    private Integer number;
    //文件md5加密值
    private String encryptvalue;
    @LogicDelete
    private Boolean deleted;
}
java

/**
 * 文件空间表
* @className: FileSpace
* @author: lijx
* @date: 2020/7/1 11:11
* @version: 1.0
*/
@Table(name = "file_space")
public class FileSpace implements AclEntity<Integer> {

    public static transient final String SPACECODE_DEFAULT = "defaultspace";//默认空间的code
    public static transient final String SPACECODE_TEMP = "tempspace";//临时空间的code

    public static transient final String NAME_DEFAULT = "默认文件空间";//默认空间的name
    public static transient final String NAME_TEMP = "临时文件空间";//临时空间的name


    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    //文件空间名称
    private String name;
    //文件空间拼音
    private String namepy;
    //文件空间编码
    private String spacecode;
    //系统名称空间
    private String namespace;
    //文件生命周期
    private Integer lifecycle;
    //拥有者,可用于查询
    private String owner;
    @OrderBy
    private Integer orderby;
    private Date createtime;
    //访问策略: 1.公开读写  2.公开读-私有写 3.私有读写 4.默认规则
    private Integer accesstype;
    @LogicDelete
    private Boolean deleted;

常量

java

public interface FileSystemConstant {

    String FILE_NAMESPACE = "file";
    String FILE_PUBLIC_SPACE = "publicspace";

    /**
     * 数字类型是否:0.否  1.是
     */
    Integer FALSE_NUM = 0;
    Integer TRUE_NUM = 1;

    /**
     * 是否
     */
    Boolean FALSE = false;
    Boolean TRUE = true;

    /**
     * 访问类型:1.公开读写  2.公开读-私有写 3.私有读写 4.默认规则
     */
    Integer ACCESSTYPE_PUBLIC = 1;
    Integer ACCESSTYPE_PUBLICREAD_PRIVATEWRITE = 2;
    Integer ACCESSTYPE_PRIVATE = 3;
    Integer ACCESSTYPE_DEFAULT = 4;

    /**
     * 有效期常量
     */
    Integer LIFECYCLE_FOREVER = 0;

    /**
     * 授权对象类型:1.文件系统 2.文件控件 3.文件
     */
    Integer  ACLENTITYTYPE_SYSTEM = 1;
    Integer  ACLENTITYTYPE_SPACE = 2;
    Integer  ACLENTITYTYPE_FILE = 3;


    /**
     * 授权类型
     * 1.读      对于文件空间或文件有读的权限
     * 2.写      对于文件空间或文件有写的权限
     * 4.列表    对于文件空间或文件有查询列表的权限
     */
    Integer AUTHTYPE_READ = 1;//读
    Integer AUTHTYPE_WRITE = 2;//写
    Integer AUTHTYPE_LIST = 4;//列表


    /**
     * IP校验正则
     */
    String IPS = "^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}$";

}

处理程序

java

import com.commnetsoft.file.model.FileEntity;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;

/**
 * 读取文件处理程序
 */
public interface IFileReadHandler extends IFileHandler{

    /**
     * 自定义url scheme
     * @param
     * @return java.lang.String
     * @author wuzm
     * @date 2020/7/8
     */
    String fileUrlSchema();

    /**
     * 文件下载
     *
     * @param response 响应
     * @param fileEntity 文件实体
     * @return void
     * @author lijx
     * date 2020/7/7 9:54
     */
    void download(HttpServletResponse response, String uri, FileEntity fileEntity) throws IOException;

    /**
     * 读取文件
     * @param response 响应
     * @param uri 文件路径
     * @param fileEntity 文件实体
     * @return void
     * @author wuzm
     * @date 2020/7/9
     */
    void readFile(HttpServletResponse response, String uri, FileEntity fileEntity) throws IOException;

    /**
     * 读取文件
     * @param fileEntity 文件实体对象
     * @param uri 文件路径
     * @return java.io.InputStream
     * @author wuzm
     * @date 2020/8/3
     */
    InputStream readFile(String uri, FileEntity fileEntity) throws IOException;

}
java

/**
 * 写文件的处理程序
 */
public interface IFileWriteHandler extends IFileHandler{

    /**
     * 文件处理类类型
     * @param
     * @return java.lang.String
     * @author wuzm
     * @date 2020/7/1
     */
    String fileLocation();

    /**
     * 保存文件
     * @param file 文件
     * @param fileSpace 文件空间
     * @param fileEntity 文件实体
     * @return uri 保存后文件的路径
     * @author wuzm
     * @date 2020/7/1
     */
    Result<String> save(MultipartFile file, FileSpace fileSpace, FileEntity fileEntity);

    /**
     * 删除文件
     * @param fileEntity 文件实体
     * @return com.commnetsoft.commons.Result
     * @author wuzm
     * @date 2020/7/13
     */
    Result delete(FileEntity fileEntity, List<FileChunk> fileChunks);
}

fastdfs处理程序

java

import com.alibaba.fastjson.JSON;
import com.commnetsoft.commons.Result;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.exception.MicroRuntimeException;
import com.commnetsoft.file.FileError;
import com.commnetsoft.file.model.FileChunk;
import com.commnetsoft.file.model.FileEntity;
import com.commnetsoft.file.model.FileSpace;
import com.commnetsoft.file.service.FileChunkService;
import com.commnetsoft.file.service.FileEntityService;
import com.commnetsoft.file.utils.FastDFSUtil;
import com.commnetsoft.file.utils.IFileReadHandler;
import com.commnetsoft.file.utils.IFileWriteHandler;
import org.apache.commons.collections.CollectionUtils;
import org.apache.http.HttpStatus;
import org.csource.common.MyException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @ClassName FastDFSFileHandler
 * @Author wuzm
 * @Date 2020/6/30 19:21
 * @Version 1.0
 */
@Component
@RefreshScope
public class FastDFSFileHandler implements IFileWriteHandler, IFileReadHandler {
    //FastDFS跟踪服务器访问地址,多个地址用,隔开
    @Value("#{fileConfig.fastdfsTrackerServerPath}")
    private String fastdfsTrackerServerPath;

    //nginx服务器访问地址
    @Value("#{fileConfig.nginxServerPath}")
    private String nginxServerPath;

    private final String TYPE_DOWNLOAD = "download";
    private final String TYPE_READ = "read";

    @Autowired
    private FileChunkService fileChunkService;
    @Autowired
    private FileEntityService fileEntityService;


    private static final Logger log = LoggerFactory.getLogger(FastDFSFileHandler.class);

    @Override
    public String fileLocation() {
        return "fastdfs";
    }

    @Override
    public String fileUrlSchema() {
        return "fastdfs";
    }


    @Override
    public Result<String> save(MultipartFile file, FileSpace fileSpace, FileEntity fileEntity) {
        try {
            return Result.create(FastDFSUtil.uploadFile(fastdfsTrackerServerPath, file, fileEntity.getSuffix()));
        } catch (MyException | IOException e) {
            log.error("文件保存失败!filespace={},fileentity={}", JSON.toJSONString(fileSpace), JSON.toJSONString(fileEntity), e);
            return Result.create(FileError.file_upload_fail);
        }
    }

    @Override
    public Result delete(FileEntity fileEntity,List<FileChunk> fileChunks) {
        try {
            List<String> fileUrs =new ArrayList<>();
            if (CollectionUtils.isNotEmpty(fileChunks)){
                //分片文件的url
                fileUrs = fileChunks.stream().map(FileChunk::getUrl).collect(Collectors.toList());
            }

            if(null != fileEntity){
                //非分片文件的url
                fileUrs.add(fileEntity.getUrl());
            }
            if (CollectionUtils.isEmpty(fileUrs)) {
                return Result.create(CommonError.forbidden, "未找到要删除的文件");
            }
            return FastDFSUtil.batchDeleteFile(fastdfsTrackerServerPath, fileUrs);
        } catch (MyException | IOException e) {
            log.error("文件删除失败!fileentity={}", JSON.toJSONString(fileEntity), e);
            return Result.create(CommonError.internal_error);
        }
    }

    @Override
    public void download(HttpServletResponse response, String uri, FileEntity fileEntity) throws IOException {
        downloadOrReadFile(response, uri, fileEntity, TYPE_DOWNLOAD);
    }

    @Override
    public void readFile(HttpServletResponse response, String uri, FileEntity fileEntity) throws IOException {
        downloadOrReadFile(response, uri, fileEntity, TYPE_READ);
    }

    @Override
    public InputStream readFile(String uri, FileEntity fileEntity) throws IOException {
        if (StringUtils.isBlank(nginxServerPath)) {
            throw new MicroRuntimeException(CommonError.forbidden, "nginx访问fastdfs服务器地址未配置!");
        }
        List<String> fileUrls = new ArrayList<>();
        if (isChunkFile(fileEntity, null)) {
            List<String> chunkFileUrls = getChunkFileUrls(fileEntity, null);
            if (CollectionUtils.isEmpty(chunkFileUrls)) {
                throw new MicroRuntimeException(CommonError.forbidden, "未找到要分片的文件信息");
            }
            fileUrls.addAll(chunkFileUrls);
        } else {
            fileUrls.add(fileEntity.getUrl());
        }
        return FastDFSUtil.readFile(nginxServerPath, fileUrls);
    }


    private void downloadOrReadFile(HttpServletResponse response, String uri, FileEntity fileEntity, String type) throws IOException {
        String filename = getFilename(uri, fileEntity);
        try {
            //分块合成
            if (isChunkFile(fileEntity, uri)) {
                List<String> fileUrls = getChunkFileUrls(fileEntity, uri);
                if (CollectionUtils.isEmpty(fileUrls)) {
                    response.sendError(HttpStatus.SC_FORBIDDEN, "未找到要下载的分片文件");
                    return;
                }
                FastDFSUtil.mergeChunks(response, fastdfsTrackerServerPath, fileUrls, filename);
            } else {
                String url = getPath(uri);
                if (StringUtils.equals(TYPE_READ, type) || (StringUtils.equals(TYPE_DOWNLOAD, type) && StringUtils.isBlank(nginxServerPath))) {
                    FastDFSUtil.downLoadFile(fastdfsTrackerServerPath, response, url, filename);
                } else if (StringUtils.equals(TYPE_DOWNLOAD, type)) {
                    FastDFSUtil.downLoadFileByRedirect(nginxServerPath, response, url, filename);
                } else {
                    throw new MicroRuntimeException(CommonError.forbidden, "获取实体文件失败");
                }
            }
            String contentType = getFileContentType(fileEntity, filename);
            response.setHeader("content-type", contentType);
            response.setContentType(contentType);
        } catch (MyException e) {
            log.error("文件下载或读取失败uri={},filename={}", uri, filename, e);
            response.sendError(HttpStatus.SC_INTERNAL_SERVER_ERROR, "文件下载或读取失败");
        }
    }


    private List<String> getChunkFileUrls(FileEntity fileEntity, String url) {
        String uploadid = getChunkFileUploadid(fileEntity, url);
        if (StringUtils.isBlank(uploadid)) {
            return null;
        }
        if (null == fileEntity) {
            fileEntity = fileEntityService.getFileEntityByUploadId(uploadid);
            if (null == fileEntity) {
                return null;
            }
        }
        List<FileChunk> fileChunks = fileChunkService.listFileChunkByFileid(fileEntity.getId());
        if (CollectionUtils.isEmpty(fileChunks)) {
            return null;
        }
        return fileChunks.stream().map(FileChunk::getUrl).collect(Collectors.toList());
    }

}
java

import com.commnetsoft.commons.Result;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.exception.MicroRuntimeException;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.http.HttpStatus;
import org.csource.common.MyException;
import org.csource.fastdfs.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.*;

/**
* @className: FastDFSUtil 操作fastDFS服务器工具类
* @author: lijx
* @date: 2020/7/8 16:11
* @version: 1.0
*/
public class FastDFSUtil {

    public static final String FASTDFS_URL_PREFIX = "fastdfs:///";


    /**
     * 获取操作远程文件服务器的对象
     * @param fastdfsTrackerServerPath 远程跟踪服务器的地址
     * @return org.csource.fastdfs.StorageClient
     * @author lijx
     * date 2020/7/8 16:18
     */
    public static StorageClient getStorageClient(String fastdfsTrackerServerPath) throws MyException, IOException {
        if (StringUtils.isBlank(fastdfsTrackerServerPath)) {
            throw new MicroRuntimeException(CommonError.illegal_config, "未配置FastDFS服务器地址");
        }
        ClientGlobal.initByTrackers(fastdfsTrackerServerPath);
        TrackerClient trackerClient = new TrackerClient();
        TrackerServer trackerServer = trackerClient.getTrackerServer();
        StorageServer storeStorage = trackerClient.getStoreStorage(trackerServer);
        return new StorageClient(trackerServer, storeStorage);
    }


    /**
     * 文件上传
     * @param fastdfsTrackerServerPath 远程跟踪服务器的地址
     * @param file              要上传的文件
     * @param fileExtName       文件扩展名
     * @return java.lang.String 返回文件地址(前缀/组名/文件地址,例如:fastdfs://group1/M00/00/00/wKgBeV8CgB6AOrWuAABHLD0PyPk795.jpg)
     * @author lijx
     * date 2020/7/8 16:26
     */
    public static String uploadFile(String fastdfsTrackerServerPath, MultipartFile file, String fileExtName) throws MyException, IOException {
        StorageClient storageClient = getStorageClient(fastdfsTrackerServerPath);
        String[] result = storageClient.upload_file(file.getBytes(), fileExtName, null);
        if (ArrayUtils.getLength(result) == 0) {
            throw new MicroRuntimeException(CommonError.illegal_response, "文件上传到FastDFS服务器,返回的地址为空");
        }
        return FASTDFS_URL_PREFIX + StringUtils.join(result, "/");
    }


    /**
     * 把分区块上传的文件块,合并成一个文件,放到response中
     * @param response
     * @param fastdfsTrackerServerPath 远程跟踪服务器的地址
     * @param fileUrls          各个区块文件的地址(一定要按顺序)
     * @param fileName          合并后的文件名称
     * @return
     * @author lijx
     * date 2020/7/8 17:26
     */
    public static void mergeChunks(HttpServletResponse response, String fastdfsTrackerServerPath, List<String> fileUrls, String fileName) throws MyException, IOException {
        if (CollectionUtils.isEmpty(fileUrls)) {
            response.sendError(HttpStatus.SC_FORBIDDEN, "下载的地址不能为空");
            return;
        }
        StorageClient storageClient = getStorageClient(fastdfsTrackerServerPath);
        if (StringUtils.isNotBlank(fileName)) {
            response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        }
        OutputStream os = response.getOutputStream();
        byte[] bytes;
        InputStream inputStream = null;
        try {
            for (String fileUrl : fileUrls) {
                String[] groupAndFileUrlArr = getGroupAndFileUrlArr(fileUrl);
                if (ArrayUtils.getLength(groupAndFileUrlArr) != 2) {
                    response.sendError(HttpStatus.SC_FORBIDDEN, "文件地址错误");
                    return;
                }
                bytes = storageClient.download_file(groupAndFileUrlArr[0], groupAndFileUrlArr[1]);
                if (ArrayUtils.isEmpty(bytes)) {
                    throw new MicroRuntimeException(CommonError.notfound, "未读到指定地址的文件");
                }
                inputStream = new ByteArrayInputStream(bytes);
                IOUtils.copy(inputStream, os);
            }
        } finally {
            if (null != inputStream) inputStream.close();
            if (null != os) os.close();
        }
    }


    /**
     * 文件下载(重定向,适用于客户端)
     * @param nginxServerPath   nginx服务器的地址
     * @param response          下载响应到客户端
     * @param fileUrl           文件存在在fastDFS的地址(例如:group1/M00/00/00/wKgBeV8CgB6AOrWuAABHLD0PyPk795.jpg)
     * @param fileName          文件名,需要带扩展名(上传到fastDFS服务器的文件,名称自动会变成无需的字符串,保证不重复)
     * @return void
     * @author lijx
     * date 2020/7/8 16:32
     */
    public static void downLoadFileByRedirect(String nginxServerPath, HttpServletResponse response, String fileUrl, String fileName) throws IOException {
        String[] groupAndFileUrlArr = getGroupAndFileUrlArr(fileUrl);
        if (ArrayUtils.getLength(groupAndFileUrlArr) != 2) {
            response.sendError(HttpStatus.SC_FORBIDDEN, "文件地址错误");
            return;
        }
        if (StringUtils.isBlank(nginxServerPath)) {
            response.sendError(HttpStatus.SC_FORBIDDEN, "未配置nginx服务器地址");
            return;
        }
        if (!StringUtils.endsWith(nginxServerPath, "/")) {
            nginxServerPath = new StringBuffer(nginxServerPath).append("/").toString();
        }
        String redirectUrl = new StringBuffer(nginxServerPath).append(groupAndFileUrlArr[0]).append("/").append(groupAndFileUrlArr[1])
                .append("?attname=").append(URLEncoder.encode(fileName, "UTF-8")).toString();
        response.sendRedirect(redirectUrl);
    }


    /**
     * 文件下载(重定向,适用于服务端)
     * @param fastdfsTrackerServerPath fastdfs跟踪服务器的地址
     * @param response          下载响应到客户端
     * @param fileUrl           文件存在在fastDFS的地址(例如:group1/M00/00/00/wKgBeV8CgB6AOrWuAABHLD0PyPk795.jpg)
     * @param fileName          文件名,需要带扩展名(上传到fastDFS服务器的文件,名称自动会变成无需的字符串,保证不重复)
     * @return void
     * @author lijx
     * date 2020/7/9 16:44
     */
    public static void downLoadFile(String fastdfsTrackerServerPath, HttpServletResponse response, String fileUrl, String fileName) throws MyException, IOException {
        if (StringUtils.isBlank(fileUrl)) {
            response.sendError(HttpStatus.SC_FORBIDDEN, "下载的文件不能为空");
            return;
        }
        mergeChunks(response, fastdfsTrackerServerPath, Arrays.asList(fileUrl), fileName);
    }


    /**
     * 批量删除服务器的文件
     * @param fastdfsTrackerServerPath  fastdfs跟踪服务器的地址
     * @param fileUrls          文件存在在fastDFS的地址(例如:group1/M00/00/00/wKgBeV8CgB6AOrWuAABHLD0PyPk795.jpg)
     * @return com.commnetsoft.commons.Result<java.lang.Object>
     * @author lijx
     * date 2020/7/9 16:48
     */
    public static Result batchDeleteFile(String fastdfsTrackerServerPath, Collection<String> fileUrls) throws MyException, IOException {
        if (CollectionUtils.isEmpty(fileUrls)) {
            return Result.create();
        }
        StorageClient storageClient = getStorageClient(fastdfsTrackerServerPath);
        for (String fileUrl : fileUrls) {
            String[] groupAndFileUrlArr = getGroupAndFileUrlArr(fileUrl);
            if (ArrayUtils.getLength(groupAndFileUrlArr) != 2) {
                return Result.create(CommonError.forbidden, "文件地址错误");
            }
            int result = storageClient.delete_file(groupAndFileUrlArr[0], groupAndFileUrlArr[1]);
            if (0 != result) {
                return Result.create(CommonError.illegal_response, "文件删除失败");
            }
        }
        return Result.create();
    }


    /**
     * explain: 读取文件
     * @param nginxServerPath   nginx服务器的地址
     * @param fileUrls  文件存在在fastDFS的地址(例如:group1/M00/00/00/wKgBeV8CgB6AOrWuAABHLD0PyPk795.jpg)
     * @author lijx
     * @date 2020/12/17
     */
    public static InputStream readFile(String nginxServerPath, List<String> fileUrls) throws IOException {
        if (CollectionUtils.isEmpty(fileUrls)) {
            throw new MicroRuntimeException(CommonError.forbidden, "读取的文件地址不能为空");
        }
        if (!StringUtils.endsWith(nginxServerPath, "/")) {
            nginxServerPath = new StringBuffer(nginxServerPath).append("/").toString();
        }
        List<InputStream> inputStreams = new ArrayList<>();
        InputStream inputStream;
        URL url;
        URLConnection uc;
        for (String fileUrl : fileUrls) {
            String[] groupAndFileUrlArr = getGroupAndFileUrlArr(fileUrl);
            if (ArrayUtils.getLength(groupAndFileUrlArr) != 2) {
                throw new MicroRuntimeException(CommonError.forbidden, "读取文件时,地址错误");
            }
            String path = new StringBuffer(nginxServerPath).append(groupAndFileUrlArr[0]).append("/").append(groupAndFileUrlArr[1]).toString();
            url = new URL(path);
            uc = url.openConnection();
            //获取输入流
            inputStream = uc.getInputStream();
            if (null == inputStream) {
                throw new MicroRuntimeException(CommonError.notfound, "未读到指定地址的文件");
            }
            inputStreams.add(inputStream);
        }
        return new SequenceInputStream(Collections.enumeration(inputStreams));
    }

    /**
     * 截取符合条件的fastDFS服务文件地址
     * @param fileUrl
     * @return java.lang.String
     * @author lijx
     * date 2020/7/8 17:17
     */
    public static String getFastDFSUrl(String fileUrl) {
        if (StringUtils.isBlank(fileUrl)) {
            return fileUrl;
        }
        if (StringUtils.contains(fileUrl, FASTDFS_URL_PREFIX)) {
            return StringUtils.replace(fileUrl, FASTDFS_URL_PREFIX, "");
        }
        return fileUrl;
    }


    /**
     * 拆分文件地址的group和文件地址
     * @param fileUrl
     * @return java.lang.String[]
     * @author lijx
     * date 2020/8/4 11:07
     */
    public static String[] getGroupAndFileUrlArr(String fileUrl) {
        String fastDFSUrl = getFastDFSUrl(fileUrl);
        if (StringUtils.isBlank(fastDFSUrl)) {
            return null;
        }
        return StringUtils.split(fastDFSUrl, "/", 2);
    }

}

http文件读取

java

import com.commnetsoft.file.model.FileEntity;
import com.commnetsoft.file.utils.IFileReadHandler;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;

/** 
 * http文件 读取
 * @ClassName HttpFileHandler
 * @Author wuzm
 * @Date 2020/7/8 14:09
 * @Version 1.0
 */
@Component
public class HttpFileHandler implements IFileReadHandler {
    @Override
    public String fileUrlSchema() {
        return "http,https";
    }

    @Override
    public void download(HttpServletResponse response, String uri, FileEntity fileEntity) throws IOException {
        response.sendRedirect(uri);
    }

    @Override
    public void readFile(HttpServletResponse response, String uri, FileEntity fileEntity) throws IOException {
        response.sendError(HttpStatus.SC_NOT_IMPLEMENTED);
    }

    @Override
    public InputStream readFile(String uri, FileEntity fileEntity) throws IOException {
        return null;
    }
}

idm文件控制程序

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.commons.utils.HttpUtils;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.exception.MicroException;
import com.commnetsoft.exception.MicroRuntimeException;
import com.commnetsoft.file.constant.FileSystemConstant;
import com.commnetsoft.file.model.FileChunk;
import com.commnetsoft.file.model.FileEntity;
import com.commnetsoft.file.model.FileSpace;
import com.commnetsoft.file.service.FileChunkService;
import com.commnetsoft.file.utils.IFileReadHandler;
import com.commnetsoft.file.utils.IFileWriteHandler;
import com.commnetsoft.file.utils.IdmFileHelper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpException;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @ClassName IdmFileHandler
 * @Author wuzm
 * @Date 2020/6/30 19:21
 * @Version 1.0
 */
@Component
@RefreshScope
public class IdmFileHandler implements IFileWriteHandler, IFileReadHandler {
    private static final Logger log = LoggerFactory.getLogger(IdmFileHandler.class);
    public static final String IDM_URL_PREFIX = "idm:///";
    //idm是否允许跨域
    @Value("#{fileConfig.idmEnableCros}")
    private Boolean idmEnableCros;

    @Autowired
    private IdmFileHelper idmFileHelper;
    @Autowired
    private FileChunkService fileChunkService;

    @Override
    public String fileLocation() {
        return "idm";
    }

    @Override
    public String fileUrlSchema() {
        return "idm";
    }

    @Override
    public Result<String> save(MultipartFile file, FileSpace fileSpace, FileEntity fileEntity) {
        try {
            Integer expires = 0;
            if (null != fileEntity.getExpiretime()) {
                expires = 1;
            }
            String uri = idmFileHelper.upload(file, fileEntity.getName(), expires);
            return Result.create(uri);
        } catch (IOException | HttpException e) {
            return Result.create(CommonError.internal_error, e.getMessage());
        } catch (MicroException e) {
            return Result.create(e);
        }
    }

    @Override
    public Result delete(FileEntity fileEntity,List<FileChunk>fileChunks) {
        return Result.create();
    }

    @Override
    public void download(HttpServletResponse response, String uri, FileEntity fileEntity) {
        try {
            uri = getPath(uri);
            if(idmEnableCros){
                response.sendRedirect(idmFileHelper.getIdmDownloadUrl(uri));
            }else{
                readFile(response, uri, fileEntity);
            }
        } catch (Exception e) {
            log.error("文件下载失败uri={}", uri, e);
        }
    }

    @Override
    public void readFile(HttpServletResponse response, String uri, FileEntity fileEntity) throws IOException {
        String url = idmFileHelper.getIdmDownloadUrl(getPath(uri));
        URL realUrl = new URL(url);
        HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();

        HttpUtils.setHeader(conn, null);

        // 发送POST请求必须设置如下两行
        conn.setDoOutput(false);
        conn.setDoInput(true);
        // 定义BufferedReader输入流来读取URL的响应
        int statusCode = conn.getResponseCode();
        if(HttpStatus.SC_OK == statusCode){
            Map<String, List<String>> headerFields = conn.getHeaderFields();
            if(MapUtils.isNotEmpty(headerFields)){
                for (Map.Entry<String, List<String>> headerFieldEntity : headerFields.entrySet()) {
                    response.setHeader(headerFieldEntity.getKey(), CollectionUtils.isNotEmpty(headerFieldEntity.getValue())  ? headerFieldEntity.getValue().get(0) : "");
                }
            }
            IOUtils.copy(conn.getInputStream(), response.getOutputStream());
            return ;
        }
        throw new IOException( url + " 请求失败:" + statusCode);
    }

    @Override
    public InputStream readFile(String uri, FileEntity fileEntity) throws IOException {
        if (FileSystemConstant.TRUE_NUM.equals(fileEntity.getChunk())){
            return readChunkFile(fileEntity);
        }
        String url = "/" + StringUtils.substringAfter(fileEntity.getUrl(), IDM_URL_PREFIX);
        url = idmFileHelper.getIdmDownloadUrl(url);
        return HttpUtils.get(url,null);
    }

    private InputStream readChunkFile(FileEntity fileEntity) throws IOException {
        FileChunk query = new FileChunk();
        query.setFileid(fileEntity.getId());
        List<FileChunk> fileChunks = fileChunkService.queryList(query);
        if (CollectionUtils.isEmpty(fileChunks)){
            throw  new MicroRuntimeException(CommonError.notfound,"分片文件不存在");
        }
        List<String> chunkUrls = fileChunks.stream().map(FileChunk::getUrl).collect(Collectors.toList());
        byte [] result=null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        for (String chunkUrl : chunkUrls) {
            String url = "/" + StringUtils.substringAfter(chunkUrl, IDM_URL_PREFIX);
            url = idmFileHelper.getIdmDownloadUrl(url);
            InputStream inputStream = HttpUtils.get(url, null);
            result=new byte[inputStream.available()];
            baos.write(result,0,inputStream.read(result));
        }
        return new ByteArrayInputStream(baos.toByteArray());
    }
}
java

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.commnetsoft.commons.utils.HttpUtils;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.commons.utils.UUIDUtils;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.exception.MicroException;
import com.commnetsoft.exception.MicroRuntimeException;
import org.apache.commons.io.FileUtils;
import org.apache.http.HttpException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * IDM文件工具类
 * @author sunwen
 * @since 2019/12/26
 */
@Component
@RefreshScope
public class IdmFileHelper {

    //idm地址
    @Value("#{fileConfig.idmHost}")
    private String idmHost;
    //idm文件存放目录ID
    @Value("#{fileConfig.idmDir}")
    private Integer idmDir;
    //idm临时文件存放目录ID
    @Value("#{fileConfig.idmTempDir}")
    private Integer idmTempDir;

    public String getIdmDownloadUrl(String path) {
        return idmHost + path;
    }

    /**
     * 上传文件到IDM 返回文件下载地址
     * @author sunwen
     * @since 2019/12/26
     */
    public String upload(MultipartFile file, String filename, Integer expires) throws IOException, HttpException, MicroException {
        Integer dirid = expires != null && expires > 0 ? idmTempDir : idmDir;
        if(dirid == null){
            throw new MicroException(CommonError.illegal_config, "未配置IDM文件目录");
        }
        if(StringUtils.isEmpty(filename)){
            filename = file.getOriginalFilename();
        }
        File localFile = null;
        try {
            File path = new File(System.getProperty("java.io.tmpdir"));
            localFile = new File(path, UUIDUtils.generate() + ".temp");
            FileUtils.copyInputStreamToFile(file.getInputStream(), localFile);
            Map<String, Object> params = new HashMap<>();
            params.put("dirid", dirid);
            params.put("file", HttpUtils.fromFile(localFile, filename));
            String result = HttpUtils.postMultipart(idmHost + "/sys/file/upload", params);
            if(StringUtils.isNotEmpty(result)){
                JSONObject obj = JSON.parseObject(result);
                if("0".equals(obj.getString("code"))){
                    String uri = "idm:///sys/file/download/" + obj.getInteger("attr");
                    /*if(StringUtils.isNotEmpty(filename)){
                        uri += "?filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8.toString());
                    }*/
                    return uri;
                }else{
                    throw new MicroException(CommonError.illegal_response, obj.getString("msg"));
                }
            }
            throw new MicroException(CommonError.unknown);
        }finally {
            FileUtils.deleteQuietly(localFile);
        }
    }


    /**
     * 根据文件id,获取文件名称
     * @param fileid
     * @return java.lang.String
     * @author lijx
     * date 2020/8/6 13:39
     */
    public String getFileNameByFileId(Integer fileid) throws IOException {
        if (null == fileid) {
            throw new MicroRuntimeException(CommonError.illegal_args, "文件id不能为空!");
        }
        if (StringUtils.isBlank(idmHost)) {
            throw new MicroRuntimeException(CommonError.illegal_config, "未配置idm地址!");
        }

        String result = HttpUtils.sendGet(idmHost + "/sys/file/getnames?ids=" + fileid, null);
        if (StringUtils.isBlank(result)) {
            throw new MicroRuntimeException(CommonError.unknown, "调用idm接口,返回值为空");
        }
        List<Map> maps = JSON.parseArray(result, Map.class);
        if (maps.size() == 0 || null == maps.get(0).get("text")) {
            throw new MicroRuntimeException(CommonError.illegal_response, "未查询到文件信息");
        }
        return maps.get(0).get("text").toString();
    }

}

本地文件处理类

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.commons.utils.UUIDUtils;
import com.commnetsoft.commons.utils.UriUtil;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.exception.MicroRuntimeException;
import com.commnetsoft.file.constant.FileSystemConstant;
import com.commnetsoft.file.model.FileChunk;
import com.commnetsoft.file.model.FileEntity;
import com.commnetsoft.file.model.FileSpace;
import com.commnetsoft.file.service.FileChunkService;
import com.commnetsoft.file.utils.IFileReadHandler;
import com.commnetsoft.file.utils.IFileWriteHandler;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 本地文件处理类
 *
 * @ClassName LocalFileHandler
 * @Author wuzm
 * @Date 2020/6/30 19:21
 * @Version 1.0
 */
@Component
@RefreshScope
public class LocalFileHandler implements IFileWriteHandler, IFileReadHandler {
    private static final Logger log = LoggerFactory.getLogger(LocalFileHandler.class);
    public static final String LOCAL_URL_PREFIX = "local:///";

    @Autowired
    private FileChunkService fileChunkService;

    //本地文件保存地址
    @Value("#{fileConfig.localSavePath}")
    private String localSavePath;

    @Override
    public String fileLocation() {
        return "local";
    }

    @Override
    public String fileUrlSchema() {
        return "local";
    }

    @Override
    public Result<String> save(MultipartFile file, FileSpace fileSpace, FileEntity fileEntity) {
        if (StringUtils.isBlank(localSavePath)) {
            return Result.create(CommonError.illegal_config, "文件上传路径配置错误");
        }
        String localPath = UriUtil.subUriLastSlash(localSavePath);
        StringBuffer realPath = new StringBuffer();
        realPath.append(localPath);
        realPath.append(File.separatorChar);
        realPath.append(fileSpace.getNamespace());
        realPath.append(File.separatorChar);
        realPath.append(fileSpace.getSpacecode());
        //再加一层目录做文件分散,防止单个文件夹下数量太大
        realPath.append(File.separatorChar);
        realPath.append(getFileSubDir(fileEntity));

        //判断文件保存的目录是否存在
        File catalog = new File(realPath.toString());
        if (!catalog.exists()) {
            catalog.mkdirs();
        }

        String filename = "";
        InputStream inputStream =null;
        FileOutputStream outputStream =null;
        try {
            if (FileSystemConstant.TRUE_NUM.equals(fileEntity.getChunk())) {
                filename = UUIDUtils.generate()+((StringUtils.isBlank(fileEntity.getSuffix())) ? "" : "." + fileEntity.getSuffix());
            } else {
                filename = fileEntity.getUploadid()+((StringUtils.isBlank(fileEntity.getSuffix())) ? "" : "." + fileEntity.getSuffix());
            }

            String s = LOCAL_URL_PREFIX + realPath + File.separatorChar + filename;
            String url = s.replaceAll("\\\\", "/");

            inputStream = file.getInputStream();
            File uploadFile = new File(catalog, filename);
            outputStream = new FileOutputStream(uploadFile);
            IOUtils.copy(inputStream, outputStream);

            return Result.create(url);
        } catch (IOException e) {
            log.error("文件上传失败!", e);
            return Result.create(CommonError.internal_error, "文件上传失败");
        }finally {
            if (inputStream !=null && outputStream != null){
                try {
                    inputStream.close();
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private String getFileSubDir(FileEntity fileEntity){
        String subDir = "00";
        if(null != fileEntity){
            if (StringUtils.isNotBlank(fileEntity.getEncryptvalue())) {
                subDir =  (fileEntity.getEncryptvalue().hashCode() & 0xf) + "";
            } else if(StringUtils.isNotBlank(fileEntity.getChunkencryptvalue())){
                subDir = (fileEntity.getChunkencryptvalue().hashCode() & 0xf) + "";
            }
        }
        return StringUtils.leftPad(subDir, 2, "0");
    }


    @Override
    public Result delete(FileEntity fileEntity,List<FileChunk> fileChunks) {
        List<String> urls = new ArrayList<>();
        if(null != fileEntity){
            urls.add(fileEntity.getUrl());
        }
        if(CollectionUtils.isNotEmpty(fileChunks)){
            for (FileChunk fileChunk : fileChunks) {
                urls.add(fileChunk.getUrl());
            }
        }
        return deleteFileByUrls(urls);
    }

    @Override
    public void download(HttpServletResponse response, String uri, FileEntity fileEntity) throws IOException {
        String filename = getFilename(uri, fileEntity);
        try {
            //判断先合并分片文件还是直接下载
            choose(response, uri, fileEntity, filename);
        } catch (IOException e) {
            log.error("文件下载失败uri={},filename={}", uri, filename, e);
            response.sendError(HttpStatus.SC_INTERNAL_SERVER_ERROR, "下载文件失败");
        }
    }

    @Override
    public void readFile(HttpServletResponse response, String uri, FileEntity fileEntity) throws IOException {
        download(response, uri, fileEntity);
    }

    private void choose(HttpServletResponse response, String uri, FileEntity fileEntity, String filename) throws IOException {
        //分块合成
        if (FileSystemConstant.TRUE_NUM.equals(fileEntity.getChunk())) {
            List<FileChunk> fileChunks = fileChunkService.listFileChunkByFileid(fileEntity.getId());
            if (CollectionUtils.isEmpty(fileChunks)) {
                response.sendError(HttpStatus.SC_NOT_FOUND, "找不到合成文件的分片");
            }
            mergeChunks(response, fileChunks, fileEntity,filename);
        } else {
            downLoadFile(response, uri, fileEntity,filename);
        }
    }

    private void mergeChunks(HttpServletResponse response, List<FileChunk> fileChunks, FileEntity fileEntity, String filename) throws IOException {
        String contentType = getFileContentType(fileEntity, filename);
        response.setHeader("content-type", contentType);
        response.setContentType(contentType);
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
        OutputStream os = response.getOutputStream();
        List<String> fileUrls = fileChunks.stream().map(FileChunk::getUrl).collect(Collectors.toList());
        InputStream inputStream = null;
        try {
            for (String fileUrl : fileUrls) {
                fileUrl = StringUtils.substringAfter(fileUrl, LOCAL_URL_PREFIX);
                File file = new File(fileUrl);
                if (!file.exists()) {
                    throw new MicroRuntimeException(CommonError.notfound, "未读到指定地址的文件");
                }
                inputStream = new FileInputStream(file);
                IOUtils.copy(inputStream, os);
            }
        } finally {
            if (null != inputStream) inputStream.close();
            if (null != os) os.close();
        }
    }

    public void downLoadFile(HttpServletResponse response, String uri, FileEntity fileEntity,String filename) {
        FileInputStream fis = null;
        try {
            String location = StringUtils.substringAfter(uri, LOCAL_URL_PREFIX);
            File file = new File(location);
            if (!file.exists()) {
                log.error("文件不存在 filename={}", filename);
                return;
            }
            fis = new FileInputStream(file);
            String contentType = getFileContentType(fileEntity, filename);
            response.setHeader("content-type", contentType);
            response.setContentType(contentType);
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
            IOUtils.copy(fis, response.getOutputStream());
            response.flushBuffer();
        }catch (ClientAbortException cae){
            log.error("客户端提前关闭异常,uri={}", uri, cae);
        }catch (IOException e) {
            log.error("文件下载失败! uri={}", uri, e);
            try {
                response.sendError(HttpStatus.SC_INTERNAL_SERVER_ERROR, "下载文件失败");
            } catch (IOException ex) {
                log.error("发送错误信息失败! uri={}", uri, ex);
            }
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    log.error("文件下载失败! uri={}", uri, e);
                }
            }
        }
    }

    @Override
    public InputStream readFile(String uri, FileEntity fileEntity) throws IOException {
        if (FileSystemConstant.TRUE_NUM.equals(fileEntity.getChunk())) {
            FileChunk query = new FileChunk();
            query.setFileid(fileEntity.getId());
            List<FileChunk> fileChunks = fileChunkService.queryList(query);
            List<String> fileUrls = fileChunks.stream().map(FileChunk::getUrl).collect(Collectors.toList());
            List<InputStream> inputStreams = new ArrayList<>();
            InputStream inputStream = null;
            for (String fileUrl : fileUrls) {
                fileUrl = StringUtils.substringAfter(fileUrl, LOCAL_URL_PREFIX);
                File file = new File(fileUrl);
                if (!file.exists()) {
                    throw new MicroRuntimeException(CommonError.notfound, "未读到指定地址的文件");
                }
                inputStream = new FileInputStream(file);
                inputStreams.add(inputStream);
            }
            return new SequenceInputStream(Collections.enumeration(inputStreams));
        }
        String url = StringUtils.substringAfter(fileEntity.getUrl(), LOCAL_URL_PREFIX);
        File file = new File(url);
        if (!file.exists()) {
            throw new MicroRuntimeException(CommonError.notfound, "未读到指定地址的文件");
        }
        return new FileInputStream(file);
    }

    private byte[] readChunkFile(List<FileChunk> fileChunks) throws IOException {
        List<String> fileUrls = fileChunks.stream().map(FileChunk::getUrl).collect(Collectors.toList());
        byte[] result = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        for (String fileUrl : fileUrls) {
            fileUrl = StringUtils.substringAfter(fileUrl, LOCAL_URL_PREFIX);
            File file = new File(fileUrl);
            if (!file.exists()) {
                throw new MicroRuntimeException(CommonError.notfound, "未读到指定地址的文件");
            }
            InputStream inputStream = new FileInputStream(file);
            result = new byte[inputStream.available()];
            baos.write(result, 0, inputStream.read(result));
        }
        return baos.toByteArray();
    }

    private Result deleteFileByUrls(List<String> urls){
        if (CollectionUtils.isEmpty(urls)){
            return Result.create();
        }
        for (String url : urls){
            String fileUrl = StringUtils.substringAfter(url, LOCAL_URL_PREFIX);
            if(StringUtils.isBlank(fileUrl)){
                log.error("文件路径不合法,{}", url);
                return Result.create(CommonError.internal_error, "文件路径不合法");
            }
            File file = new File(fileUrl);
            if (!file.exists()){
                continue;
            }
            //删除真实的文件
            boolean delete = file.delete();
            if (delete){
                continue;
            }else{
                log.error("文件删除失败,文件路径={}", url);
                return Result.create(CommonError.internal_error, "文件删除失败");
            }
        }
        return Result.create();
    }
}

OSS文件处理

java

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.*;
import com.commnetsoft.commons.Result;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.commons.utils.UUIDUtils;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.constant.FileSystemConstant;
import com.commnetsoft.file.model.FileChunk;
import com.commnetsoft.file.model.FileEntity;
import com.commnetsoft.file.model.FileSpace;
import com.commnetsoft.file.service.FileChunkService;
import com.commnetsoft.file.utils.IFileReadHandler;
import com.commnetsoft.file.utils.IFileWriteHandler;
import org.apache.commons.collections.CollectionUtils;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @ClassName OssFileHandler
 * @Author wujs
 * @Date 2020/9/18 15:13 
 * @Version 1.0
 */
@Component
@RefreshScope
public class OssFileHandler implements IFileWriteHandler, IFileReadHandler {
    private static final Logger log = LoggerFactory.getLogger(OssFileHandler.class);

    //oss文件服务器保存地址
    @Value("#{fileConfig.ossSavePath}")
    private String endpoint;

    //oss文件服务器accessKeyId
    @Value("#{fileConfig.accessKeyId}")
    private String accessKeyId;

    //oss文件服务器accessKeySecret
    @Value("#{fileConfig.accessKeySecret}")
    private String accessKeySecret;

    //oss文件服务器bucketName
    @Value("#{fileConfig.bucketName}")
    private String bucketName;

    public static final String OSS_URL_PREFIX = "oss:///";

    @Autowired
    private FileChunkService fileChunkService;


    @Override
    public String fileUrlSchema() {
        return "oss";
    }

    @Override
    public String fileLocation() {
        return "oss";
    }

    @Override
    public Result<String> save(MultipartFile file, FileSpace fileSpace, FileEntity fileEntity) {

        if (file == null) {
            return Result.create(CommonError.illegal_args, "文件不能为空");
        }
        String fileName = null;
        //新文件名称
        if (FileSystemConstant.TRUE_NUM.equals(fileEntity.getChunk())) {
            fileName = UUIDUtils.generate() + "." + fileEntity.getSuffix();
        } else {
            fileName = fileEntity.getUploadid() + "." + fileEntity.getSuffix();
        }
        //获取文件类型
        String fileType = file.getContentType();
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //上传文件
        String url = putFile(inputStream, fileType, fileName, fileEntity);
        if (StringUtils.isNotBlank(url)) {
            return Result.create(url);
        } else {
            return Result.create(CommonError.internal_error, "文件上传到oss文件服务器失败");
        }
    }

    private String putFile(InputStream input, String fileType, String fileName, FileEntity fileEntity) {
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        try {
            // 创建上传Object的Metadata
            ObjectMetadata meta = new ObjectMetadata();
            // 设置上传内容类型
            meta.setContentType(fileType);
            //被下载时网页的缓存行为
            meta.setCacheControl("no-cache");
            //获取原文件名
            String filename = fileEntity.getName();
            //设置文件上传时的请求头信息,这样上传返回文件存储的url路径,通过url路径直接下载就可以得到源文件名而不是加密的随机码。
            meta.setContentDisposition("attachment;filename=\"" + filename + "\"");
            //创建上传请求
            PutObjectRequest request = new PutObjectRequest(bucketName, fileName, input, meta);
            //上传文件
            ossClient.putObject(request);
            //设置url过期时间
            Date expiration = null;
            if (null != fileEntity.getExpiretime()) {
                expiration = fileEntity.getExpiretime();
            } else {
                //100年
                expiration = new Date(new Date().getTime() + 1000 * 3600 * 24 * 365 * 100);
            }
            //获取上传成功的文件地址
            URL url = ossClient.generatePresignedUrl(bucketName, fileName, expiration);
            //截取url,不要accessKeyId等信息
            String[] str = url.toString().split("\\?");
            if (StringUtils.isNotBlank(str[0])) {
                return OSS_URL_PREFIX + str[0];
            } else {
                return null;
            }
        } catch (OSSException e) {
            log.error("文件上传失败!", e);
            return null;
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    @Override
    public void download(HttpServletResponse response, String uri, FileEntity fileEntity) throws IOException {
        String filename = getFilename(uri, fileEntity);
        try {
            //判断先合并分片文件还是直接下载
            choose(response, uri, fileEntity, filename);
        } catch (IOException e) {
            log.error("文件下载失败uri={},filename={}", uri, filename, e);
            response.sendError(HttpStatus.SC_INTERNAL_SERVER_ERROR, "下载文件失败");
        }
    }

    private void choose(HttpServletResponse response, String uri, FileEntity fileEntity, String filename) throws IOException {
        //分块合成
        if (FileSystemConstant.TRUE_NUM.equals(fileEntity.getChunk())) {
            List<FileChunk> fileChunks = fileChunkService.listFileChunkByFileid(fileEntity.getId());
            if (CollectionUtils.isEmpty(fileChunks)) {
                response.sendError(HttpStatus.SC_NOT_FOUND, "找不到合成文件的分片");
            }
            mergeChunks(response, fileChunks, fileEntity, filename);
        } else {
            downloadFile(response, uri, fileEntity, filename);
        }
    }

    /**
     * 合并分片
     *
     * @param response
     * @param fileChunks
     * @param fileEntity
     * @param filename
     * @return void
     * @author wujs
     * @date 2020/9/22 14:17
     */
    private void mergeChunks(HttpServletResponse response, List<FileChunk> fileChunks, FileEntity fileEntity, String filename) {

        String downName = null;
        OutputStream outputStream = null;

        try {
            List<String> fileUrls = fileChunks.stream().map(FileChunk::getUrl).collect(Collectors.toList());
            downName = new String(fileEntity.getName().getBytes("gb2312"), "ISO8859-1");
            String contentType = getFileContentType(fileEntity, filename);
            response.setHeader("content-type", contentType);
            response.setContentType(contentType);
            //设置文件下载名称
            response.addHeader("Content-Disposition", "attachment;filename=" + downName);
            outputStream = response.getOutputStream();
            byte[] bytes = readChunkFile(fileUrls);
            outputStream.write(bytes);
            log.info("《downName={}》下载成功", downName);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                log.error("IO流关闭失败");
                e.printStackTrace();
            }
        }
    }

    private byte[] readChunkFile(List<String> fileUrls) throws IOException {
        InputStream inputStream = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        for (String fileUrl : fileUrls) {
            String fileName = fileUrl.substring(fileUrl.lastIndexOf('/') + 1, fileUrl.length());
            OSSObject object = ossClient.getObject(bucketName, fileName);
            inputStream = new BufferedInputStream(object.getObjectContent());
            byte[] buffBytes = new byte[1024];
            int read = 0;
            while ((read = inputStream.read(buffBytes)) != -1) {
                baos.write(buffBytes, 0, read);
            }
        }
        inputStream.close();
        baos.close();
        ossClient.shutdown();
        return (baos.toByteArray());
    }

    /**
     * 下载文件
     *
     * @param response
     * @param uri
     * @param fileEntity
     * @param filename
     * @return void
     * @author wujs
     * @date 2020/9/22 14:18
     */
    public void downloadFile(HttpServletResponse response, String uri, FileEntity fileEntity, String filename) {
        BufferedInputStream input = null;
        OutputStream outputStream = null;
        try {
            String fileName = fileEntity.getUploadid() + "." + fileEntity.getSuffix();
            String downName = new String(fileEntity.getName().getBytes("gb2312"), "ISO8859-1");
            String contentType = getFileContentType(fileEntity, filename);
            response.setHeader("content-type", contentType);
            response.setContentType(contentType);
            //设置文件下载名称
            response.addHeader("Content-Disposition", "attachment;filename=" + downName);
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
            OSSObject object = ossClient.getObject(bucketName, fileName);
            byte[] buffBytes = new byte[1024];
            input = new BufferedInputStream(object.getObjectContent());
            outputStream = response.getOutputStream();
            int read = 0;
            while ((read = input.read(buffBytes)) != -1) {
                outputStream.write(buffBytes, 0, read);
            }
            log.info("《downName={}》下载成功", downName);
            outputStream.flush();
            ossClient.shutdown();
        } catch (IOException ex) {
            log.info("文件下载失败");
            ex.printStackTrace();
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
                if (input != null) {
                    input.close();
                }
            } catch (IOException e) {
                log.error("io流关闭失败");
                e.printStackTrace();
            }
        }
    }

    @Override
    public void readFile(HttpServletResponse response, String uri, FileEntity fileEntity) throws IOException {
        download(response, uri, fileEntity);
    }

    @Override
    public InputStream readFile(String uri, FileEntity fileEntity) throws IOException {
        if (FileSystemConstant.TRUE_NUM.equals(fileEntity.getChunk())) {
            FileChunk query = new FileChunk();
            query.setFileid(fileEntity.getId());
            List<FileChunk> fileChunks = fileChunkService.queryList(query);
            List<String> fileUrls = fileChunks.stream().map(FileChunk::getUrl).collect(Collectors.toList());
            byte[] bytes = readChunkFile(fileUrls);
            return new ByteArrayInputStream(bytes);
        } else {
            String fileName = fileEntity.getUploadid() + "." + fileEntity.getSuffix();
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
            OSSObject object = ossClient.getObject(bucketName, fileName);
            return object.getObjectContent();
        }
    }


    @Override
    public Result delete(FileEntity fileEntity, List<FileChunk> fileChunks) {
        List<String> urls = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(fileChunks)) {
            for (FileChunk fileChunk : fileChunks) {
                urls.add(fileChunk.getUrl());
            }
        }
        if (null != fileEntity) {
            urls.add(fileEntity.getUrl());
        }
        return deleteFileByUrls(urls);
    }

    /**
     * 删除oss文件服务器上的文件
     *
     * @param urls
     * @return com.commnetsoft.commons.Result
     * @author wujs
     * @date 2020/9/22 14:53
     */
    private Result deleteFileByUrls(List<String> urls) {
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        ArrayList<String> fileNames = new ArrayList<>();
        String fileName = null;
        try {
            //根据url获取fileName,通过filename删除oss服务器上的文件
            for (String fileUrl : urls) {
                fileName = fileUrl.substring(fileUrl.lastIndexOf('/') + 1, fileUrl.length());
                fileNames.add(fileName);
            }
            DeleteObjectsRequest request = new DeleteObjectsRequest(bucketName).withKeys(fileNames);
            ossClient.deleteObjects(request);
            return Result.create("删除文件成功");
        } catch (OSSException oe) {
            oe.printStackTrace();
            throw new RuntimeException("OSS服务异常:", oe);
        } catch (ClientException ce) {
            ce.printStackTrace();
            throw new RuntimeException("OSS客户端异常:", ce);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}

项目启动时将处理程序存入map

java

import com.commnetsoft.commons.utils.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @ClassName FileHandlerFactory
 * @Author wuzm
 * @Date 2020/6/30 19:20
 * @Version 1.0
 */
@Component
public class FileHandlerFactory implements ApplicationContextAware {
    private static Map<String, IFileWriteHandler> fileWriteHandlerMap;
    private static Map<String, IFileReadHandler> fileReadHandlerMap;
    private static Set<String> fileLocationList; //支持的所有文件存储类型

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, IFileWriteHandler> fileWriteMap = applicationContext.getBeansOfType(IFileWriteHandler.class);
        fileWriteHandlerMap = new HashMap<>();
        fileLocationList = new HashSet<>();
        fileWriteMap.forEach((key, value) -> {
            fileWriteHandlerMap.put(value.fileLocation(), value);
            fileLocationList.add(value.fileLocation());
        });

        Map<String, IFileReadHandler> fileReadMap = applicationContext.getBeansOfType(IFileReadHandler.class);
        fileReadHandlerMap = new HashMap<>();
        fileReadMap.forEach((key, value) -> {
            String schemeStr = value.fileUrlSchema();
            String[] schemes = StringUtils.split(schemeStr, ",");
            if (ArrayUtils.isNotEmpty(schemes)) {
                for (String scheme : schemes) {
                    fileReadHandlerMap.put(scheme, value);
                }
            }
        });
    }

    public static IFileWriteHandler getFileWriteHandler(String handlerType) {
        return fileWriteHandlerMap.get(handlerType);
    }

    public static IFileReadHandler getFileReadHandler(String scheme) {
        return fileReadHandlerMap.get(scheme);
    }

    public static Set<String> getFileLocationList() {
        return fileLocationList;
    }

}

file工具类

java

import com.commnetsoft.commons.utils.DateUtils;
import com.commnetsoft.commons.utils.FileUtils;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.commons.utils.UUIDUtils;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.exception.MicroException;
import com.commnetsoft.file.MockMultipartFile;
import com.commnetsoft.file.constant.FileSystemConstant;
import com.commnetsoft.file.model.FileSpace;
import com.commnetsoft.file.utils.filehandlerimpl.HttpFileHandler;
import com.commnetsoft.file.utils.filehandlerimpl.IdmFileHandler;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.FileNameMap;
import java.net.URI;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Date;

/**
 * @ClassName FileUtil
 * @Author wuzm
 * @Date 2020/8/6 15:45
 * @Version 1.0
 */
public class FileUtil {

    /**
     * 是否为合法的文件系统地址,uploadid或者file://uploadid是为合法的
     * @param uri
     * @return boolean
     * @author wuzm
     * @date 2020/8/7
     */
    public static boolean isFileSystemUri(String uri) {
        if (StringUtils.isBlank(uri)) {
            return false;
        }
        return StringUtils.startsWithIgnoreCase(uri, FileSystemConstant.FILE_NAMESPACE) || UUIDUtils.isUuid(uri);
    }


    /**
     * 是否为合法的文件读取地址判断
     * 只允许idm:///或file://开头以及uploadid的格式
     * @param uri 文件路径
     * @return boolean true 允许的,false 不允许的
     * @author wuzm
     * @date 2020/8/6
     */
    public static boolean isLegalReadUri(String uri) {
        if(isFileSystemUri(uri)){
            return true;
        }
        return StringUtils.startsWithIgnoreCase(uri, new IdmFileHandler().fileUrlSchema());
    }

    /**
     * 是否是合法的下载地址
     * 允许idm:///或file://或http://或https://开头以及uploadid的格式
     * @param uri 文件路径
     * @return boolean
     * @author wuzm
     * @date 2020/8/6
     */
    public static boolean isLegalDownloadUri(String uri){
        if(isLegalReadUri(uri)){
            return true;
        }
        return StringUtils.startsWithIgnoreCase(uri, new HttpFileHandler().fileUrlSchema().split(",")[0])
                || StringUtils.startsWithIgnoreCase(uri, new HttpFileHandler().fileUrlSchema().split(",")[1]);
    }


    /**
     * 将File写到response中
     * @param response
     * @param file
     * @return void
     * @author lijx
     * date 2020/8/20 8:52
     */
    public static void fileToResponse(HttpServletResponse response, File file) throws IOException {
        FileInputStream fis = new FileInputStream(file);
        BufferedInputStream bis = new BufferedInputStream(fis);
        byte[] fileByte = new byte[bis.available()];
        bis.read(fileByte);
        String fileName = file.getName();
        if (StringUtils.isNotBlank(fileName)) {
            response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        }
        OutputStream outputStream = response.getOutputStream();
        outputStream.write(fileByte);
        if (null != fis) fis.close();
        if (null != bis) bis.close();
        if (null != outputStream) {
            outputStream.flush();
            outputStream.close();
        }
    }


    /**
     * 将File转换成MultipartFile
     * @param file
     * @return org.springframework.web.multipart.MultipartFile
     * @author lijx
     * date 2020/8/20 8:50
     */
    public static MultipartFile fromFile(File file) throws IOException {
        MockMultipartFile multipartFile = null;
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            FileNameMap fileNameMap = URLConnection.getFileNameMap();
            String contentType = fileNameMap.getContentTypeFor(file.getName());
            multipartFile = new MockMultipartFile("file", file.getName(), contentType, fis);
        } finally {
            if (null != fis) fis.close();
        }
        return multipartFile;
    }


    /**
     * 把文件输入流转inputStream换成文件File
     * @param inputStream
     * @param fileName
     * @return java.io.File
     * @author lijx
     * date 2020/8/21 11:09
     */
    public static File inputStreamToFile(InputStream inputStream, String fileName) throws IOException {
        if (StringUtils.isBlank(fileName)) {
            fileName = UUIDUtils.generate();
        }
        if (!StringUtils.contains(fileName, ".")) {
            fileName += ".tmp";
        }
        File tempDirPath = new File(System.getProperty("java.io.tmpdir"));
        File tempFile = new File(tempDirPath, fileName);
        FileUtils.copyToFile(inputStream, tempFile);
        return tempFile;
    }

    /**
     * 获取文件名称
     * 如果传入的文件名不带后缀,则取源文件的文件名后缀
     * 如果传入的文件名为空,则直接返回源文件名
     * 如果传入的文件名后缀与源文件的后缀不一样,则加上源文件的后缀
     *
     * @param filename         文件名
     * @param originalFilename 源文件名
     * @return java.lang.String
     * @author wuzm
     * @date 2020/7/31
     */
    public static String getFilename(String filename, String originalFilename) {
        if (StringUtils.isBlank(filename)) {
            return originalFilename;
        } else {
            filename = StringUtils.trim(filename);
            String newFileSuffix = FileUtils.getSuffix(filename);
            String sourceSuffix = FileUtils.getSuffix(originalFilename);
            if (StringUtils.isBlank(newFileSuffix)) {
                if (StringUtils.isBlank(sourceSuffix)) {
                    return filename;
                }else{
                    return filename + "." + sourceSuffix;
                }
            }else{
                if (StringUtils.isBlank(sourceSuffix)) {
                    return filename;
                }else if(StringUtils.equals(newFileSuffix,sourceSuffix)){
                    return filename;
                }else{
                    return filename + "." + sourceSuffix;
                }
            }
        }
    }

    /**
     * 获取MultipartFile的文件名
     * @param file
     * @return java.lang.String
     * @author lijx
     * date 2020/8/21 14:34
     */
    public static String getFileName(MultipartFile file) {
        if (null == file) {
            return "";
        }
        if (StringUtils.isNotBlank(file.getOriginalFilename())) {
            return file.getOriginalFilename();
        }
        return file.getName();
    }


    /**
     * 修改文件后缀
     * @Param name
     * @Param suffix
     * @Return java.lang.String
     * @Author wuzm
     * Date 2019/12/17 14:09
     */
    public static String changeFileNameToTargetSuffix(String name,String suffix){
        if (StringUtils.startsWith(suffix, ".")) {
            suffix = StringUtils.substringAfter(suffix, ".");
        }
        String prefixName = StringUtils.substringBeforeLast(name, ".");
        return prefixName + "." +suffix;
    }

    /**
     * 将文件路径转成文件上传Id
     *
     * @param uri 文件路径
     * @return java.lang.String
     * @author wuzm
     * @date 2020/7/31
     */
    public static String getUploadidByUri(String uri) throws MicroException {
        if (UUIDUtils.isUuid(uri)) {
            return uri;
        }
        if (!StringUtils.startsWith(uri, FileSystemConstant.FILE_NAMESPACE)) {
            throw new IllegalArgumentException();
        }
        URI newUri = URI.create(uri);
        if (null == newUri) {
            throw new MicroException(CommonError.illegal_args, "无法识别的文件路径");
        }
        return newUri.getHost();
    }

    /**
     * 获取文件超时时间
     * expires:
     * 0:表示永久
     * 数字:具体超时天数
     * null:跟随文件空间的超时时间
     *
     * @param fileSpace   文件空间
     * @param expires     文件过期天数
     * @param currentTime 当前时间
     * @return java.util.Date
     * @author wuzm
     * @date 2020/7/13
     */
    public static Date getExpireTime(FileSpace fileSpace, Integer expires, Date currentTime) {
        Date expiresTime = null;
        //文件时间处理
        if (null == expires) {
            Integer lifeCycle = fileSpace.getLifecycle();
            if (null != lifeCycle && lifeCycle > 0) {
                expiresTime = DateUtils.addDays(currentTime, lifeCycle);
            }
        } else if (expires > 0) {
            expiresTime = DateUtils.addDays(currentTime, expires);
        }
        return expiresTime;
    }

    public static  File inputstreamConverterFile(InputStream inputStream ,String sourceType) throws IOException {
        String fileName = UUIDUtils.generate() + "." + sourceType;
        //创建临时文件
        File tempDirPath = new File(System.getProperty("java.io.tmpdir"));
        fileName = FileUtil.changeFileNameToTargetSuffix(fileName, sourceType);
        File tempFile = new File(tempDirPath, fileName);

        //把输入流转换成和源文件格式相同的图片,存在临时文件中
        int index;
        byte[] bytes = new byte[1024];
        FileOutputStream outputStream = new FileOutputStream(tempFile);
        while ((index = inputStream.read(bytes)) != -1) {
            outputStream.write(bytes, 0, index);
            outputStream.flush();
        }
        outputStream.close();
        return tempFile;
    }
}

MultipartFile拓展实体类

java

import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

public class MockMultipartFile implements MultipartFile {

    private final String name;

    private String originalFilename;

    @Nullable
    private String contentType;

    private final byte[] content;


    /**
     * Create a new MockMultipartFile with the given content.
     * @param name the name of the file
     * @param content the content of the file
     */
    public MockMultipartFile(String name, @Nullable byte[] content) {
        this(name, "", null, content);
    }

    /**
     * Create a new MockMultipartFile with the given content.
     * @param name the name of the file
     * @param contentStream the content of the file as stream
     * @throws IOException if reading from the stream failed
     */
    public MockMultipartFile(String name, InputStream contentStream) throws IOException {
        this(name, "", null, FileCopyUtils.copyToByteArray(contentStream));
    }

    /**
     * Create a new MockMultipartFile with the given content.
     * @param name the name of the file
     * @param originalFilename the original filename (as on the client's machine)
     * @param contentType the content type (if known)
     * @param content the content of the file
     */
    public MockMultipartFile(
            String name, @Nullable String originalFilename, @Nullable String contentType, @Nullable byte[] content) {

        Assert.hasLength(name, "Name must not be null");
        this.name = name;
        this.originalFilename = (originalFilename != null ? originalFilename : "");
        this.contentType = contentType;
        this.content = (content != null ? content : new byte[0]);
    }

    /**
     * Create a new MockMultipartFile with the given content.
     * @param name the name of the file
     * @param originalFilename the original filename (as on the client's machine)
     * @param contentType the content type (if known)
     * @param contentStream the content of the file as stream
     * @throws IOException if reading from the stream failed
     */
    public MockMultipartFile(
            String name, @Nullable String originalFilename, @Nullable String contentType, InputStream contentStream)
            throws IOException {

        this(name, originalFilename, contentType, FileCopyUtils.copyToByteArray(contentStream));
    }


    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public String getOriginalFilename() {
        return this.originalFilename;
    }

    @Override
    @Nullable
    public String getContentType() {
        return this.contentType;
    }

    @Override
    public boolean isEmpty() {
        return (this.content.length == 0);
    }

    @Override
    public long getSize() {
        return this.content.length;
    }

    @Override
    public byte[] getBytes() throws IOException {
        return this.content;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        return new ByteArrayInputStream(this.content);
    }

    @Override
    public void transferTo(File dest) throws IOException, IllegalStateException {
        FileCopyUtils.copy(this.content, dest);
    }

}

文件转换

文件转换支持word、pdf、html、txt等格式之间的相互转换

工具类

wordutil

java

import com.aspose.words.Document;
import com.aspose.words.License;
import com.aspose.words.SaveFormat;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.commons.utils.UUIDUtils;
import com.commnetsoft.file.converter.AbstractFileConverter;
import com.commnetsoft.file.utils.FileUtil;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.converter.WordToTextConverter;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;

/**
 * @ClassName WordUtil
 * @Author wujs
 * @Date 2020/9/25 11:15 
 * @Version 1.0
 */
public class WordUtil {
    private final static Logger logger = LoggerFactory.getLogger(WordUtil.class);


    /**
     * 支持docy与docx互转,doc和docx转pdf
     *
     * @param inputStream
     * @param type
     * @return com.commnetsoft.commons.Result<java.io.File>
     * @author wujs
     * @date 2020/8/24 14:07
     */
    public static File wordConvert(InputStream inputStream, String type) {
        File tempDirPath = new File(System.getProperty("java.io.tmpdir"));
        File pdffile = new File(tempDirPath, UUIDUtils.generate() + "." + type);
        FileOutputStream fileOS = null;
        // 验证License
        if (!getLicense()) {
            logger.error("验证License失败!");
            return null;
        }
        try {
            Document doc = new Document(inputStream);
            fileOS = new FileOutputStream(pdffile);
            // 保存转换的pdf文件
            if (AbstractFileConver
            ter.FILE_TYPE_PDF.equals(type)) {
                doc.save(fileOS, SaveFormat.PDF);
            } else if (AbstractFileConverter.FILE_TYPE_DOC.equals(type)) {
                doc.save(fileOS, SaveFormat.DOC);
            } else {
                doc.save(fileOS, SaveFormat.DOCX);
            }
            return pdffile;
        } catch (Exception e) {
            logger.error("error:", e);
        } finally {
            try {
                if (fileOS != null) {
                    fileOS.close();
                }
            } catch (IOException e) {
                logger.error("error:", e);
            }
        }
        return null;
    }

    public static boolean getLicense() {
        boolean result = false;
        try {
            // 凭证
            String licenseStr = "<License>\n" + "  <Data>\n" + "    <Products>\n"
                    + "      <Product>Aspose.Total for Java</Product>\n"
                    + "      <Product>Aspose.Words for Java</Product>\n" + "    </Products>\n"
                    + "    <EditionType>Enterprise</EditionType>\n"
                    + "    <SubscriptionExpiry>20991231</SubscriptionExpiry>\n"
                    + "    <LicenseExpiry>20991231</LicenseExpiry>\n"
                    + "    <SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber>\n" + "  </Data>\n"
                    + "  <Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=</Signature>\n"
                    + "</License>";
            InputStream license = new ByteArrayInputStream(licenseStr.getBytes("UTF-8"));
            License asposeLic = new License();
            asposeLic.setLicense(license);
            result = true;
        } catch (Exception e) {
            logger.error("error:", e);
        }
        return result;
    }

    /**
     * word转txt
     *
     * @param inputStream
     * @param targetFileType
     * @return java.io.File
     * @author wujs
     * @date 2020/9/24 16:28
     */
    public static File wordToTxt(InputStream inputStream, String targetFileType, String sourceType) throws IOException {
        String text = null;
        try {
            if (StringUtils.equals(AbstractFileConverter.FILE_TYPE_DOCX, sourceType)) {
                XWPFDocument document = new XWPFDocument(inputStream);
                XWPFWordExtractor wordExtractor = new XWPFWordExtractor(document);
                text = wordExtractor.getText();
                if (null != document) {
                    document.close();
                }
                if (null != wordExtractor) {
                    wordExtractor.close();
                }
            }
            if (StringUtils.equals(AbstractFileConverter.FILE_TYPE_DOC, sourceType)) {
                HWPFDocument documentDoc = new HWPFDocument(inputStream);
                StringBuilder sb = documentDoc.getText();
                if (null != sb) {
                    text = sb.toString();
                }
                if (null != documentDoc) {
                    documentDoc.close();
                }
            }

        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
        File tempDirPath = new File(System.getProperty("java.io.tmpdir"));
        File txtFile = new File(tempDirPath, UUIDUtils.generate() + "." + targetFileType);
        if (StringUtils.isBlank(text)) {
            return txtFile;
        }
        byte[] bytes = text.getBytes("UTF-8");
        OutputStream os = new FileOutputStream(txtFile);
        os.write(bytes);
        if (os != null) {
            os.flush();
            os.close();
        }
        return txtFile;
    }


    /**
     * txt文件转word文件
     * @param inputStreamn
     * @param type
     * @return java.io.File
     * @author lijx
     * date 2020/8/28 10:33
     */
    public static File txtToWordByType(InputStream inputStreamn, String type) throws IOException {
        String fileName = UUIDUtils.generate();
        File tempDirPath = new File(System.getProperty("java.io.tmpdir"));
        File wordFile = new File(tempDirPath, FileUtil.changeFileNameToTargetSuffix(fileName, type));
        FileOutputStream os = null;
        try {
            os = new FileOutputStream(wordFile);
            XWPFDocument xwpfDocument = new XWPFDocument();
            XWPFParagraph paragraph = xwpfDocument.createParagraph();
            XWPFRun run = paragraph.createRun();
            run.setText(readTxt(inputStreamn));
            xwpfDocument.write(os);
            xwpfDocument.close();
        } finally {
            if (os != null) {
                os.flush();
                os.close();
                inputStreamn.close();
            }
        }
        return wordFile;
    }

    /**
     * 读取text文件的内容
     * @param inputStream
     * @return java.lang.String
     * @author lijx
     * date 2020/8/28 9:25
     */
    public static String readTxt(InputStream inputStream) throws IOException {
        StringBuilder builder = new StringBuilder();
        String line; // 用来保存每行读取的内容
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
        line = reader.readLine(); // 读取第一行
        while (line != null) { // 如果 line 为空说明读完了
            builder.append(line + "\r\n"); // 将读到的内容添加到 builder 中
            line = reader.readLine(); // 读取下一行
        }
        reader.close();
        inputStream.close();
        return builder.toString();
    }

    /**
     * 将doc文件转成txt文本文件
     *
     * @param inputStream
     * @param targetType
     * @return java.io.File
     * @author wujs
     * @date 2020/9/29 11:23
     */
    public static File docToTxt(InputStream inputStream, String targetType) throws Exception {

        WordToTextConverter converter = new WordToTextConverter(DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument());
        HWPFDocument wordDocument = new HWPFDocument(inputStream);
        converter.processDocument(wordDocument);
        //对HWPFDocument进行转换
        String fileName = UUIDUtils.generate();
        File tempDirPath = new File(System.getProperty("java.io.tmpdir"));
        File wordFile = new File(tempDirPath, FileUtil.changeFileNameToTargetSuffix(fileName, targetType));
        Writer writer = new FileWriter(wordFile);
        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        transformer.setOutputProperty(OutputKeys.ENCODING, "utf-8");
        //是否添加空格
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty(OutputKeys.METHOD, "text");
        transformer.transform(
                new DOMSource(converter.getDocument()),
                new StreamResult(writer));
        return wordFile;
    }

    /**将docx文件转成TXT文件
     *
     * @param inputStream
     * @param targetType
     * @return
     * @throws Exception
     */
    public static File docxToTxt(InputStream inputStream, String targetType) throws Exception {
        //先把docx文件转成doc文件
        File file = wordConvert(inputStream, AbstractFileConverter.FILE_TYPE_DOC);
        FileInputStream fileInputStream = new FileInputStream(file);
        File txtFile = docToTxt(fileInputStream, targetType);
        return txtFile;
    }
}

Transform

java

import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.*;

import java.util.HashMap;

/**
 * @ClassName Transform
 * @Author wujs
 * @Date 2020/9/29 17:52
 * @Version 1.0
 */
public class Transform {

    private int lastColumn = 0;
    private HashMap<Integer, HSSFCellStyle> styleMap = new HashMap();

    public void transformXSSF(XSSFWorkbook workbookOld, HSSFWorkbook workbookNew) {
        HSSFSheet sheetNew;
        XSSFSheet sheetOld;

        workbookNew.setMissingCellPolicy(workbookOld.getMissingCellPolicy());

        for (int i = 0; i < workbookOld.getNumberOfSheets(); i++) {
            sheetOld = workbookOld.getSheetAt(i);
            sheetNew = workbookNew.getSheet(sheetOld.getSheetName());
            sheetNew = workbookNew.createSheet(sheetOld.getSheetName());
            this.transform(workbookOld, workbookNew, sheetOld, sheetNew);
        }
    }

    private void transform(XSSFWorkbook workbookOld, HSSFWorkbook workbookNew,
                           XSSFSheet sheetOld, HSSFSheet sheetNew) {

        sheetNew.setDisplayFormulas(sheetOld.isDisplayFormulas());
        sheetNew.setDisplayGridlines(sheetOld.isDisplayGridlines());
        sheetNew.setDisplayGuts(sheetOld.getDisplayGuts());
        sheetNew.setDisplayRowColHeadings(sheetOld.isDisplayRowColHeadings());
        sheetNew.setDisplayZeros(sheetOld.isDisplayZeros());
        sheetNew.setFitToPage(sheetOld.getFitToPage());

        sheetNew.setHorizontallyCenter(sheetOld.getHorizontallyCenter());
        sheetNew.setMargin(Sheet.BottomMargin,
                sheetOld.getMargin(Sheet.BottomMargin));
        sheetNew.setMargin(Sheet.FooterMargin,
                sheetOld.getMargin(Sheet.FooterMargin));
        sheetNew.setMargin(Sheet.HeaderMargin,
                sheetOld.getMargin(Sheet.HeaderMargin));
        sheetNew.setMargin(Sheet.LeftMargin,
                sheetOld.getMargin(Sheet.LeftMargin));
        sheetNew.setMargin(Sheet.RightMargin,
                sheetOld.getMargin(Sheet.RightMargin));
        sheetNew.setMargin(Sheet.TopMargin, sheetOld.getMargin(Sheet.TopMargin));
        sheetNew.setPrintGridlines(sheetNew.isPrintGridlines());
        sheetNew.setRightToLeft(sheetNew.isRightToLeft());
        sheetNew.setRowSumsBelow(sheetNew.getRowSumsBelow());
        sheetNew.setRowSumsRight(sheetNew.getRowSumsRight());
        sheetNew.setVerticallyCenter(sheetOld.getVerticallyCenter());

        HSSFRow rowNew;
        for (Row row : sheetOld) {
            rowNew = sheetNew.createRow(row.getRowNum());
            if (rowNew != null)
                this.transform(workbookOld, workbookNew, (XSSFRow) row, rowNew);
        }

        for (int i = 0; i < this.lastColumn; i++) {
            sheetNew.setColumnWidth(i, sheetOld.getColumnWidth(i));
            sheetNew.setColumnHidden(i, sheetOld.isColumnHidden(i));
        }

        for (int i = 0; i < sheetOld.getNumMergedRegions(); i++) {
            CellRangeAddress merged = sheetOld.getMergedRegion(i);
            sheetNew.addMergedRegion(merged);
        }
    }

    private void transform(XSSFWorkbook workbookOld, HSSFWorkbook workbookNew,
                           XSSFRow rowOld, HSSFRow rowNew) {
        HSSFCell cellNew;
        rowNew.setHeight(rowOld.getHeight());

        for (Cell cell : rowOld) {
            cellNew = rowNew.createCell(cell.getColumnIndex(),
                    cell.getCellType());
            if (cellNew != null)
                this.transform(workbookOld, workbookNew, (XSSFCell) cell,
                        cellNew);
        }
        this.lastColumn = Math.max(this.lastColumn, rowOld.getLastCellNum());
    }

    private void transform(XSSFWorkbook workbookOld, HSSFWorkbook workbookNew,
                           XSSFCell cellOld, HSSFCell cellNew) {
        cellNew.setCellComment(cellOld.getCellComment());

        Integer hash = cellOld.getCellStyle().hashCode();
        if (this.styleMap != null && !this.styleMap.containsKey(hash)) {
            this.transform(workbookOld, workbookNew, hash,
                    cellOld.getCellStyle(),
                    (HSSFCellStyle) workbookNew.createCellStyle());
        }
        cellNew.setCellStyle(this.styleMap.get(hash));

        switch (cellOld.getCellType()) {
            case BLANK:
                break;
            case BOOLEAN:
                cellNew.setCellValue(cellOld.getBooleanCellValue());
                break;
            case ERROR:
                cellNew.setCellValue(cellOld.getErrorCellValue());
                break;
            case FORMULA:
                cellNew.setCellValue(cellOld.getCellFormula());
                break;
            case NUMERIC:
                cellNew.setCellValue(cellOld.getNumericCellValue());
                break;
            case STRING:
                cellNew.setCellValue(cellOld.getStringCellValue());
                break;
            default:
                System.out.println("transform: Unbekannter Zellentyp "
                        + cellOld.getCellType());
        }
    }

    private void transform(XSSFWorkbook workbookOld, HSSFWorkbook workbookNew,
                           Integer hash, XSSFCellStyle styleOld, HSSFCellStyle styleNew) {
        styleNew.setAlignment(styleOld.getAlignment());
        styleNew.setBorderBottom(styleOld.getBorderBottom());
        styleNew.setBorderLeft(styleOld.getBorderLeft());
        styleNew.setBorderRight(styleOld.getBorderRight());
        styleNew.setBorderTop(styleOld.getBorderTop());
        styleNew.setDataFormat(this.transform(workbookOld, workbookNew,
                styleOld.getDataFormat()));
        styleNew.setFillBackgroundColor(styleOld.getFillBackgroundColor());
        styleNew.setFillForegroundColor(styleOld.getFillForegroundColor());
        styleNew.setFillPattern(styleOld.getFillPattern());
        styleNew.setFont(this.transform(workbookNew,
                (XSSFFont) styleOld.getFont()));
        styleNew.setHidden(styleOld.getHidden());
        styleNew.setIndention(styleOld.getIndention());
        styleNew.setLocked(styleOld.getLocked());
        styleNew.setVerticalAlignment(styleOld.getVerticalAlignment());
        styleNew.setWrapText(styleOld.getWrapText());
        this.styleMap.put(hash, styleNew);
    }

    private short transform(XSSFWorkbook workbookOld, HSSFWorkbook workbookNew,
                            short index) {
        DataFormat formatOld = workbookOld.createDataFormat();
        DataFormat formatNew = workbookNew.createDataFormat();
        return formatNew.getFormat(formatOld.getFormat(index));
    }

    private HSSFFont transform(HSSFWorkbook workbookNew, XSSFFont fontOld) {
        HSSFFont fontNew = workbookNew.createFont();
        fontNew.setBold(true);
        fontNew.setCharSet(fontOld.getCharSet());
        fontNew.setColor(fontOld.getColor());
        fontNew.setFontName(fontOld.getFontName());
        fontNew.setFontHeight(fontOld.getFontHeight());
        fontNew.setItalic(fontOld.getItalic());
        fontNew.setStrikeout(fontOld.getStrikeout());
        fontNew.setTypeOffset(fontOld.getTypeOffset());
        fontNew.setUnderline(fontOld.getUnderline());
        return fontNew;
    }
}

PictureUtil

java

import com.aspose.words.Document;
import com.aspose.words.ImageSaveOptions;
import com.aspose.words.SaveFormat;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.commons.utils.UUIDUtils;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.exception.MicroRuntimeException;
import com.commnetsoft.file.converter.AbstractFileConverter;
import com.commnetsoft.file.utils.FileUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @className: PictureUtil
 * @author: lijx
 * @date: 2020/8/21 14:54
 * @version: 1.0
 */
public class PictureUtil {
    private static final Logger log = LoggerFactory.getLogger(PictureUtil.class);

    public static final String FILE_TYPE_ZIP = "zip";

    /**
     * jpg、jpeg、png格式互转
     *
     * @param inputStream
     * @param targetType
     * @param sourceType
     * @return java.io.File
     * @author wujs
     * @date 2020/9/27 9:54
     */
    public static File pictureConvertByType(InputStream inputStream, String targetType, String sourceType) throws IOException {

        //把inputstream转换成file存在临时目录中
        File pictureFile = FileUtil.inputstreamConverterFile(inputStream, sourceType);

        //创建按目标格式转换成功后文件存储的目录
        File catalog = new File(System.getProperty("java.io.tmpdir"));
        File tempFile = new File(catalog, UUIDUtils.generate() + "." + targetType);
        OutputStream outputStream = new FileOutputStream(tempFile);
        BufferedImage bufferedImage = ImageIO.read(pictureFile);
        BufferedImage newBufferedImage = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), BufferedImage.TYPE_INT_RGB);

        newBufferedImage.createGraphics().drawImage(bufferedImage, 0, 0, Color.WHITE, null);
        ImageIO.write(newBufferedImage, targetType, tempFile);
        if (null != bufferedImage) bufferedImage.flush();
        if (null != newBufferedImage) newBufferedImage.flush();
        inputStream.close();
        outputStream.flush();
        outputStream.close();
        //删除原文件
        pictureFile.delete();
        return tempFile;
    }

    /**
     * 将pdf转换成图片,如果不超出10张,会将图片拼成长图,如果超过10张,转成zip压缩包返回
     * @param inputStream
     * @param type
     * @return java.io.File
     * @author lijx
     * date 2020/8/25 15:02
     */
    public static File pdfToPictureByType(InputStream inputStream, String type) throws IOException {

        File tempDirPath = new File(System.getProperty("java.io.tmpdir"));
        PDDocument pdDocument = PDDocument.load(inputStream);
        try {
            PDFRenderer renderer = new PDFRenderer(pdDocument);
            int pages = pdDocument.getNumberOfPages();
            if (pages < 1) {
                throw new MicroRuntimeException(CommonError.forbidden, "未找到pdf的内容");
            }
            if (StringUtils.startsWith(type, ".")) {
                type = StringUtils.substringAfter(type, ".");
            }
            List<File> files = new ArrayList<>();
            BufferedImage image = null;
            String fileName = UUIDUtils.generate();
            for (int i = 0; i < pages; i++) {
                File tempFile = new File(tempDirPath, fileName + i + "." + type);
                image = renderer.renderImageWithDPI(i, 144);
                if (null != image) {
                    ImageIO.write(image, type, tempFile);
                    files.add(tempFile);
                }
            }
            if (null != image) image.flush();
            return returnFile(files, tempDirPath, fileName, type);
        } finally {
            pdDocument.close();
        }
    }


    private static File returnFile(List<File> files, File tempDirPath, String fileName, String type) throws IOException {
        try {
            if (CollectionUtils.isEmpty(files)) {
                throw new MicroRuntimeException(CommonError.forbidden, "未找到转换后的图片信息");
            }
            File resultFile = null;
            if (files.size() > 10) {
                resultFile = packgePicture(tempDirPath, files, FileUtil.changeFileNameToTargetSuffix(fileName, AbstractFileConverter.FILE_TYPE_ZIP));
            } else {
                File changeFile = mergePicture(files, type);
                resultFile = new File(tempDirPath.getPath() + "/" + FileUtil.changeFileNameToTargetSuffix(fileName, type));
                FileUtils.copyFile(changeFile, resultFile);
                if (changeFile.exists()) {
                    changeFile.delete();
                }
            }
            return resultFile;
        } finally {
            files.parallelStream().filter(chunkFile -> chunkFile.exists()).forEach(File::delete);
        }
    }

    /**
     * 将多张图片合成长图
     * @param files
     * @param suffix
     * @return java.io.File
     * @author lijx
     * date 2020/8/25 15:06
     */
    public static File mergePicture(List<File> files, String suffix) throws IOException {
        if (files.size() == 1) {
            return files.get(0);
        }
        if (StringUtils.startsWith(suffix, ".")) {
            suffix = StringUtils.substringAfter(suffix, ".");
        }
        File tempFile = null;
        for (int i = 1; i < files.size(); i++) {
            if (i == 1) {
                tempFile = iterableMergePicture(files.get(0), files.get(1), suffix);
            } else {
                tempFile = iterableMergePicture(tempFile, files.get(i), suffix);
            }
        }
        return tempFile;
    }


    /**
     * 迭代合成图片
     * @param upFile
     * @param downFile
     * @param suffix
     * @return java.io.File
     * @author lijx
     * date 2020/8/25 15:06
     */
    private static File iterableMergePicture(File upFile, File downFile, String suffix) throws IOException {
        if (null == upFile || !upFile.exists()) {
            return downFile;
        }
        if (null == downFile || !downFile.exists()) {
            return upFile;
        }
        List<File> files = new ArrayList<>();
        files.add(upFile);
        files.add(downFile);
        Integer allWidth = 0;    // 图片总宽度
        Integer allHeight = 0;    // 图片总高度
        List<BufferedImage> imgs = new ArrayList<>();
        BufferedImage read = null;
        for (int i = 0; i < files.size(); i++) {
            read = ImageIO.read(files.get(i));
            if (null != read) {
                imgs.add(read);
                //竖向
                if (i == 0) {
                    allWidth = imgs.get(0).getWidth();
                }
                allHeight += imgs.get(i).getHeight();
            }
        }
        if (null != read) {
            read.getGraphics().dispose();
            read.flush();
        }
        BufferedImage combined = new BufferedImage(allWidth, allHeight, BufferedImage.TYPE_INT_RGB);
        Graphics g = combined.getGraphics();
        // 竖向合成
        Integer height = 0;
        for (int i = 0; i < imgs.size(); i++) {
            g.drawImage(imgs.get(i), 0, height, null);
            height += imgs.get(i).getHeight();
        }
        if (combined != null && upFile != null) {
            ImageIO.write(combined, suffix, upFile);
        }
        g.dispose();
        combined.flush();
        return upFile;
    }


    /**
     * 将文件打包成一个zip文件
     * @param tempDirPath
     * @param files
     * @param packgeName
     * @return java.io.File
     * @author lijx
     * date 2020/8/25 15:06
     */
    public static File packgePicture(File tempDirPath, List<File> files, String packgeName) throws IOException {
        File tempZip = new File(tempDirPath, packgeName);
        FileOutputStream outputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        ZipOutputStream zipOutputStream = null;
        BufferedInputStream bufferedInputStream = null;
        InputStream input = null;
        int length;
        byte[] readByte = new byte[1024];
        try {
            outputStream = new FileOutputStream(tempZip);
            bufferedOutputStream = new BufferedOutputStream(outputStream);
            zipOutputStream = new ZipOutputStream(outputStream);
            zipOutputStream.setEncoding("GBK");
            for (int i = 0; i < files.size(); i++) {
                input = new FileInputStream(files.get(i));
                bufferedInputStream = new BufferedInputStream(input);
                zipOutputStream.putNextEntry(new ZipEntry(files.get(i).getName()));
                while ((length = bufferedInputStream.read(readByte)) > 0) {
                    zipOutputStream.write(readByte, 0, length);
                }
                bufferedInputStream.close();
            }
        } finally {
            if (null != zipOutputStream) zipOutputStream.closeEntry();
            if (null != zipOutputStream) zipOutputStream.close();
            if (null != bufferedOutputStream) bufferedOutputStream.close();
            if (null != outputStream) outputStream.close();
            if (null != bufferedInputStream) bufferedInputStream.close();
            if (null != input) input.close();
        }
        return tempZip;
    }


    /**
     * word文件转图片,如果不超出10张,会将图片拼成长图,如果超过10张,转成zip压缩包返回
     * @param inputStream
     * @param type
     * @return java.io.File
     * @author lijx
     * date 2020/8/25 16:50
     */
    public static File wordToPictureByType(InputStream inputStream, String type) {
        try {
            Document document = new Document(inputStream);
            ImageSaveOptions options = new ImageSaveOptions(SaveFormat.PNG);
            int pageCount = document.getPageCount();
            File tempDirPath = new File(System.getProperty("java.io.tmpdir"));
            String fileName = UUIDUtils.generate();
            List<File> files = new ArrayList<>();
            for (int i = 0; i < pageCount; i++) {
                File tempFile = new File(tempDirPath, fileName + i + "." + type);
                FileOutputStream os = new FileOutputStream(tempFile);
                options.setPageIndex(i);
                document.save(os, options);
                files.add(tempFile);
                if (os != null) os.close();
            }
            return returnFile(files, tempDirPath, fileName, type);
        } catch (Exception e) {
            log.error("word转图片失败", e);
            throw new MicroRuntimeException(CommonError.forbidden, "word转图片失败");
        }
    }


    /**
     * 图片转pdf
     * @param inputStream
     * @return java.io.File
     * @author lijx
     * date 2020/8/27 11:29
     */
    public static File pictureToPdf(InputStream inputStream, String type) throws IOException {
        String fileName = UUIDUtils.generate();
        PDDocument document = new PDDocument();
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        byte[] buffBytes = new byte[1024];
        int read = 0;
        while ((read = inputStream.read(buffBytes)) != -1) {
            stream.write(buffBytes, 0, read);
        }
        PDImageXObject fromByteArray = PDImageXObject.createFromByteArray(document, stream.toByteArray(), fileName);
        float height = fromByteArray.getHeight();
        float width = fromByteArray.getWidth();
        float x = 0;
        float y = 0;
        PDRectangle a4 = PDRectangle.A4;
        if (height < a4.getHeight()) {
            y = a4.getHeight() - height;
            height = a4.getHeight();
        }
        if (width < a4.getWidth()) {
            width = a4.getWidth();
        }

        PDRectangle pdRectangle = new PDRectangle(width, height);
        PDPage page = new PDPage(pdRectangle);
        document.addPage(page);
        PDPageContentStream contentStream = new PDPageContentStream(document, page);
        contentStream.drawImage(fromByteArray, x, y);
        contentStream.close();
        File tempDirPath = new File(System.getProperty("java.io.tmpdir"));
        File tempFile = new File(tempDirPath, UUIDUtils.generate() + "." + type);
        document.save(tempFile);
        document.close();
        return tempFile;
    }

}

PdfUtil

java

import com.aspose.pdf.Document;
import com.aspose.pdf.License;
import com.aspose.pdf.SaveFormat;
import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.commons.utils.UUIDUtils;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.exception.MicroException;
import com.commnetsoft.exception.MicroRuntimeException;
import com.commnetsoft.file.converter.AbstractFileConverter;
import com.commnetsoft.file.converter.util.docxconverter.DocxHelper;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.xml.bind.JAXBException;
import java.io.*;

/**
 * @ClassName PdfUtil
 * @Author wujs
 * @Date 2020/9/24 15:53 
 * @Version 1.0
 */
@Component
public class PdfUtil {
    private final static Logger logger = LoggerFactory.getLogger(PdfUtil.class);

    @Autowired
    private PDFHelper pdfHelper;
    @Autowired
    private DocxHelper docxHelper;


    public static File pdfToWord(InputStream inputStream, String targetFileType) throws IOException {
        if (!getLicense()) {
            logger.error("验签失败!");
            return null;
        }
        FileOutputStream os = null;
        try {
            File catalog = new File(System.getProperty("java.io.tmpdir"));
            String filePath = catalog + "\\" + UUIDUtils.generate() + "." + targetFileType;
            Document pdfDocument = new Document(inputStream);
            os = new FileOutputStream(filePath);
            if (StringUtils.equals(AbstractFileConverter.FILE_TYPE_DOC, targetFileType)) {
                pdfDocument.save(os, SaveFormat.Doc);
            } else {
                pdfDocument.save(os, SaveFormat.DocX);
            }
            File file = new File(filePath);
            return file;
        } catch (Exception e) {
            logger.error("转换失败!", e);
        } finally {
            inputStream.close();
            if (null != os) {
                os.close();
            }
        }
        return null;
    }

    public static File txtToPdf(InputStream inputStream, String targetType) throws IOException {
////        Document document = new Document();
////        //创建存储pdf的临时文件
////        File catalog = new File(System.getProperty("java.io.tmpdir"));
////        String filePath = catalog + "\\" + UUIDUtils.generate() + "." + targetType;
////        OutputStream outputStream = new FileOutputStream(new File(filePath));
////        PdfWriter.getInstance(document, outputStream);
////        document.open();
////        //方法一:使用Windows系统字体(TrueType)
////        //字体设置
////        BaseFont bf = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
////
////        //创建Font对象,将基础字体对象,字体大小,字体风格
////        Font font = new Font(bf, 13, Font.NORMAL);
////        InputStreamReader isr = new InputStreamReader((inputStream), "UTF-8");
////        BufferedReader bufferedReader = new BufferedReader(isr);
////        String str = "";
////        while ((str = bufferedReader.readLine()) != null) {
////            document.add(new Paragraph(str, font));
////        }
////        document.close();
////        inputStream.close();
////        outputStream.close();
////        File tempFile = new File(filePath);
//
//        return tempFile;
        throw new MicroRuntimeException(CommonError.unimplement);
    }

    private static boolean getLicense() {
        boolean result = false;
        try {
            // 凭证
            String licenseStr = "<License>\n" + "  <Data>\n" + "    <Products>\n"
                    + "      <Product>Aspose.Total for Java</Product>\n"
                    + "      <Product>Aspose.Pdf for Java</Product>\n"
                    + "    </Products>\n"
                    + "    <EditionType>Enterprise</EditionType>\n"
                    + "    <SubscriptionExpiry>29991231</SubscriptionExpiry>\n"
                    + "    <LicenseExpiry>29991231</LicenseExpiry>\n"
                    + "    <SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber>\n" + "  </Data>\n"
                    + "  <Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=</Signature>\n"
                    + "</License>";
            InputStream license = new ByteArrayInputStream(licenseStr.getBytes("UTF-8"));
            License asposeLic = new License();
            asposeLic.setLicense(license);
            result = true;
        } catch (Exception e) {
            logger.error("error:", e);
        }
        return result;
    }

    public static String getHtmlString(InputStream inputStream) {
        String htmlString = "";
        InputStreamReader read = null;
        BufferedReader reader = null;
        try {
            read = new InputStreamReader(inputStream, "UTF-8");
            reader = new BufferedReader(read);
            String line;
            while ((line = reader.readLine()) != null) {
                htmlString += line;
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != read) {
                try {
                    read.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (null != reader) {
                try {
                    reader.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return htmlString;
    }

    public File convert(InputStream inputStream, String targetFileType) throws IOException, MicroException, JAXBException, Docx4JException {
        String htmlString = getHtmlString(inputStream);
        File catalog = new File(System.getProperty("java.io.tmpdir"));
        String filePath = catalog + "\\" + UUIDUtils.generate() + "." + targetFileType;
        FileOutputStream outputStream = new FileOutputStream(filePath);
        if (StringUtils.equals(targetFileType, "docx")) {
            docxHelper.convert(htmlString, null, false, null, outputStream);
        }
        if (StringUtils.equals(targetFileType, "pdf")) {
            pdfHelper.convert(htmlString, null, false, null, outputStream);
        }
        File file = new File(filePath);
        return file;
    }

}

PDFHelper

java

import com.commnetsoft.commons.utils.StringUtils;
import com.commnetsoft.commons.utils.UUIDUtils;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.exception.MicroException;
import com.commnetsoft.exception.MicroRuntimeException;
import com.commnetsoft.file.model.HtmlSheetDto;
import com.itextpdf.forms.PdfPageFormCopier;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.io.codec.Base64;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.*;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.canvas.draw.DottedLine;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.*;
import com.itextpdf.layout.font.FontProvider;
import com.itextpdf.layout.property.*;
import org.apache.commons.lang3.BooleanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * PDF转换工具类
 * @author sunwen
 * @since 2019/12/13
 */
@Component
@RefreshScope
public class PDFHelper {

    private static final Logger log = LoggerFactory.getLogger(PDFHelper.class);

    @Value("#{fileConfig.fontsPath}")
    private String fontsPath;

    private static Map<String, PageSize> pagerMap = new HashMap<>();

    static {
        pagerMap.put("A0", PageSize.A0);
        pagerMap.put("A1", PageSize.A1);
        pagerMap.put("A2", PageSize.A2);
        pagerMap.put("A3", PageSize.A3);
        pagerMap.put("A4", PageSize.A4);
        pagerMap.put("A5", PageSize.A5);
        pagerMap.put("A6", PageSize.A6);
        pagerMap.put("A7", PageSize.A7);
        pagerMap.put("A8", PageSize.A8);
        pagerMap.put("A9", PageSize.A9);
        pagerMap.put("A10", PageSize.A10);

        pagerMap.put("B0", PageSize.B0);
        pagerMap.put("B1", PageSize.B1);
        pagerMap.put("B2", PageSize.B2);
        pagerMap.put("B3", PageSize.B3);
        pagerMap.put("B4", PageSize.B4);
        pagerMap.put("B5", PageSize.B5);
        pagerMap.put("B6", PageSize.B6);
        pagerMap.put("B7", PageSize.B7);
        pagerMap.put("B8", PageSize.B8);
        pagerMap.put("B9", PageSize.B9);
        pagerMap.put("B10", PageSize.B10);
    }

    private ConverterProperties converterProperties;

    private ConverterProperties getConverterProperties(){
        if(converterProperties == null){
            converterProperties = new ConverterProperties();
            FontProvider fontProvider = new FontProvider();
            fontProvider.addStandardPdfFonts();
            fontProvider.addDirectory(fontsPath);
//            fontProvider.addFont(fontsPath + "/SIMSUN.TTF");//宋体
//            fontProvider.addFont(fontsPath + "/SIMKAI.TTF");//楷体
//            fontProvider.addFont(fontsPath + "/SIMHEI.TTF");//黑体
            converterProperties.setFontProvider(fontProvider);
            converterProperties.setCharset("UTF-8");
        }
        return converterProperties;
    }

    /**
     * 创建文档
     * @author sunwen
     * @since 2020/12/10
     */
    private static Document createDocumnet(PdfWriter pdfWriter, String pageSize, boolean rotate, Integer[] margins) throws MicroException {
        DocumentProperties properties = new DocumentProperties();
        PdfDocument pdfDocument = new PdfDocument(pdfWriter, properties);
        PageSize ps;
        if(StringUtils.isEmpty(pageSize)){
            ps = PageSize.Default;
        }else{
            ps = pagerMap.get(pageSize);
            if(ps == null){//TODO 支持 200X300的格式自定义页面大小
                throw new MicroException(CommonError.illegal_args, "不支持的页面大小:"+ pageSize);
            }
        }
        if(rotate){
            ps = ps.rotate();
        }
        pdfDocument.setDefaultPageSize(ps);
        Document document = new Document(pdfDocument);
        float defaultMargin = 60 * 72 / 96f;
        float[] ms = new float[]{defaultMargin, defaultMargin, defaultMargin, defaultMargin};
        if(margins != null){
            for(int i = 0; i < margins.length && i < ms.length; i++){
                Integer m = margins[i];
                if(m != null && m >= 0){
                    ms[i] = m * 72 / 96f;
                }
            }
        }
        document.setMargins(ms[0], ms[1], ms[2], ms[3]);
        return document;
    }

    /**
     * 将一个html转换成pdf文件
     * @author sunwen
     * @since 2019/12/25
     */
    public void convert(String html, String pageSize, boolean rotate, Integer[] margins, OutputStream out) throws IOException, MicroException {
        try (Document document = createDocumnet(new PdfWriter(out), pageSize, rotate, margins)) {
            List<IElement> list = HtmlConverter.convertToElements(html, getConverterProperties());
            for (IElement ie : list) {
                setTable(ie);
                IBlockElement element = (IBlockElement) ie;
                element.setProperty(Property.SPLIT_CHARACTERS, ChineseSplitCharaters.instance);
                document.add(element);
            }
        }
    }

    /**
     * 将多个html转换成pdf文件
     * @param sheet 页面
     * @author sunwen
     * @since 2019/12/25
     */
    public void convert(HtmlSheetDto sheet, OutputStream out) throws IOException, MicroException {
        try (Document document = createDocumnet(
                new PdfWriter(out),
                sheet.getPagesize(),
                sheet.isRotate(),
                sheet.getMargins()
        )) {
            PdfFont font = PdfFontFactory.createFont(fontsPath + "/SIMSUN.TTF", PdfEncodings.IDENTITY_H, true);
            PdfFont titlefont = PdfFontFactory.createFont(fontsPath + "/SIMHEI.TTF", PdfEncodings.IDENTITY_H,true);
            PageIndexEvent event = new PageIndexEvent(font);
            document.getPdfDocument().addEventHandler(PdfDocumentEvent.END_PAGE, event);
            addSheet(document, getConverterProperties(), titlefont, sheet, null, true);
        }
    }

    /**
     * 将多个html转换成带封面及目录的pdf文件
     * @param book 根节点的html:封面 (封面如果超过一页将不显示) 为空不设置封面;children:内容页列表
     * @param autOrdinal 目录是否需要自动编号
     * @author sunwen
     * @since 2019/12/25
     */
    public void convertBook(HtmlSheetDto book, boolean autOrdinal, OutputStream out) throws IOException, MicroException {
        //先转内容获取页码
        File contentFile = new File(System.getProperty("java.io.tmpdir"), UUIDUtils.generate() + ".cont");
        long t = System.currentTimeMillis();
        Document contentDoc = createDocumnet(new PdfWriter(contentFile), book.getPagesize(), book.isRotate(), book.getMargins());
        PdfFont font = PdfFontFactory.createFont(fontsPath + "/SIMSUN.TTF", PdfEncodings.IDENTITY_H,true);
        List<Catalog> catalogs = new ArrayList<>();
        try {
            PageIndexEvent event = new PageIndexEvent(font);
            contentDoc.getPdfDocument().addEventHandler(PdfDocumentEvent.END_PAGE, event);
            boolean isBlank = true;
            List<HtmlSheetDto> contentSheets = book.getChildren();
            for(HtmlSheetDto item : contentSheets){
                Catalog catalog = new Catalog();
                isBlank = addSheet(contentDoc, getConverterProperties(), font, item, catalog, isBlank);
                catalogs.add(catalog);
            }
        }finally {
            try {
                contentDoc.close();
            }catch (Exception e){
                //Empty
            }
        }

        long t1 = System.currentTimeMillis();
        Document document = createDocumnet(new PdfWriter(out), book.getPagesize(), book.isRotate(), book.getMargins());
        try {
            //添加目录
            PdfFont titlefont = PdfFontFactory.createFont(fontsPath + "/SIMHEI.TTF", PdfEncodings.IDENTITY_H,true);
            document.add(new Paragraph("目录").setFont(titlefont).setTextAlignment(TextAlignment.CENTER).setFontSize(18));
            String ordinal = autOrdinal ? "" : null;
            addCatalog(document, catalogs, ordinal,0, titlefont);
            //添加封面(先添加封面移除多余页页后添加内容会NPE)
            PdfDocument pdfDoc = document.getPdfDocument();
            if (StringUtils.isNotEmpty(book.getHtml())) {
                int end = pdfDoc.getNumberOfPages();
                document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
                List<IElement> list = HtmlConverter.convertToElements(book.getHtml(), converterProperties);
                for (IElement ie : list) {
                    setTable(ie);
                    IBlockElement element = (IBlockElement) ie;
                    element.setProperty(Property.SPLIT_CHARACTERS, ChineseSplitCharaters.instance);
                    document.add(element);
                }
                int n = pdfDoc.getNumberOfPages();//封面移前
                for (int i = n; i > end; i--) {
                    if (i == end + 1) {
                        pdfDoc.movePage(i, 1);
                    } else {
                        pdfDoc.removePage(i);
                    }
                }
            }
            long t2 = System.currentTimeMillis();
            PdfDocument content = new PdfDocument(new PdfReader(contentFile));
            content.copyPagesTo(1, content.getNumberOfPages(), pdfDoc, new PdfPageFormCopier());
            content.close();
            contentFile.delete();
            long t3 = System.currentTimeMillis();
            log.info("PDF转换耗时{}ms,其中生成内容{}ms,生成封面及目录{}ms,拷贝内容{}ms", t3 - t, t1 - t, t2 - t1, t3 - t2);
        }finally {
            try {
                document.close();
            }catch (Exception e){
                //Empty
            }
        }
    }

    /**
     * @param catalog 目录列表
     * @param isBlank 当前是否在一个空白页上
     * @author sunwen
     * @since 2020/12/10
     */
    private static boolean addSheet(Document document, ConverterProperties properties, PdfFont font,
                                    HtmlSheetDto sheet, Catalog catalog, boolean isBlank) throws IOException {
        if(!isBlank && BooleanUtils.isTrue(sheet.getNewpage())){ //当前不在新页面 设置了分页,插入分页符
            document.add(new AreaBreak());
            isBlank = true;
        }
        int pageNum = document.getPdfDocument().getNumberOfPages();//FIXME sunwen 当前页写不下时换页,页码可能不准确
        String image = sheet.getImage();
        if(catalog != null && StringUtils.isEmpty(image)){//图片存在该页视为内容,不设置目录标题
            catalog.setTitle(sheet.getTitle());
            catalog.setPageNum(Math.max(1, pageNum));
        }
        boolean showTitle = BooleanUtils.isTrue(sheet.getShowtitle());
        if(StringUtils.isNotEmpty(image)){
            isBlank = false;
            String[] strParts = image.split(",");
            if(strParts.length == 2 && ("data:image/gif;base64".equals(strParts[0])
                    || "data:image/png;base64".equals(strParts[0]) || "data:image/jpeg;base64".equals(strParts[0]))){
                float titleHeight = 0f;
//                log.error(document.getRenderer().getCurrentArea().getBBox().toString());
                if(showTitle && StringUtils.isNotEmpty(sheet.getTitle())){
                    Paragraph title = new Paragraph(sheet.getTitle()).setFont(font);
                    title.setTextAlignment(TextAlignment.CENTER).setFontSize(16);
                    title.setKeepWithNext(true);//标题和图片不分页,
                    document.add(title);
                    titleHeight = 33.894f;//FIXME sunwen 设置keep后,标题占用高度需后面添加元素后才计算,无法动态获取标题高度
                }
                Image img = new Image(ImageDataFactory.create(Base64.decode(strParts[1])));
                Rectangle areaRect = document.getRenderer().getCurrentArea().getBBox();
                Rectangle imgRect = fitImage(areaRect.getWidth(), areaRect.getHeight() - titleHeight,
                        img.getImageWidth(), img.getImageHeight());
                if(imgRect != null){
                    img.setAutoScale(true);
                    img.setMarginLeft(imgRect.getLeft()); //设置边距为了图片居中显示
                    img.setMarginRight(areaRect.getWidth() - imgRect.getRight());
                }else{
                    img.setAutoScaleWidth(true);
                }
                document.add(img);
                showTitle = false;//图片存在 标题视为图片标题,后面不设置文档标题
            }else{
                throw new MicroRuntimeException(CommonError.illegal_args, "不支持的图片格式");
            }
        }
        if(showTitle && StringUtils.isNotEmpty(sheet.getTitle())){
            //处理标题 样式
            Paragraph title = new Paragraph(sheet.getTitle()).setFont(font);
            title.setFontSize(16);//TODO 判断目录层级设置不同的字体大小
            document.add(title);
        }
        if(StringUtils.isNotEmpty(sheet.getHtml())){//允许页面内容为空(只有标题)
            isBlank = false;
            List<IElement> list = HtmlConverter.convertToElements(sheet.getHtml(), properties);
            for (IElement ie : list) {
                setTable(ie);
//                if(ie instanceof Paragraph){//FIXME sunwen 文本两端对齐无效
//                    ((Paragraph) ie).setTextAlignment(TextAlignment.JUSTIFIED);
//                }
                IBlockElement element = (IBlockElement) ie;
                element.setProperty(Property.SPLIT_CHARACTERS, ChineseSplitCharaters.instance);
                document.add(element);
            }
        }
        if(sheet.getChildren() != null){
            for(HtmlSheetDto item : sheet.getChildren()){
                Catalog cc = catalog != null ? catalog.nextCatalog() : null;
                isBlank = addSheet(document, properties, font, item, cc, isBlank);
            }
        }
        return isBlank;
    }

    /**
     * 计算图片是否可以放入容器
     * 如果可以放入返回 图片区域否则返回null
     * @author sunwen
     * @since 2020/12/22
     */
    private static Rectangle fitImage(float containerWidth, float containerHeight, float imageWidth, float imageHeight){
        float minHeight = 150f;
        float widthScale = containerWidth/imageWidth;
        float height = imageHeight*widthScale;
        if(height > minHeight && containerHeight < minHeight){//图片缩放后高度大于150 容器高度不足150时不允许放入容器
            return null;
        }
        if(height > containerHeight){//宽度适配容器后,高度超过容器,按容器高度适配
            float heightScale = containerHeight/imageHeight;
            float width = imageWidth * heightScale;
            float margin = (containerWidth - width)/2f;
            return new Rectangle(margin, 0, width, containerHeight);
        }else{
            return new Rectangle(containerWidth, containerHeight);
        }
    }

    /**
     * 递归添加目录
     * @param parentOrdinal 父级序号 传null表示不需要生成序号
     * @author sunwen
     * @since 2019/12/24
     */
    private static void addCatalog(Document document, List<Catalog> catalogs, String parentOrdinal, int deep, PdfFont font){
        if(catalogs == null){
            return;
        }
        int i = 0;
        for(Catalog catalog : catalogs) {
            String index = parentOrdinal;
            String title = catalog.getTitle();
            int _deep = deep;
            if(StringUtils.isNotEmpty(title)) {//标题存在才加入目录
                if(parentOrdinal != null){
                    index = parentOrdinal + ++i + '.';
                    title = index + title;
                }
                Paragraph p = new Paragraph(title).setFont(font).setFontSize(12).setMarginLeft(12 * deep);
                PdfDocument pdfDoc = document.getPdfDocument();
                PdfPage page = pdfDoc.getPage(pdfDoc.getNumberOfPages());
                float width = page.getPageSize().getWidth();
                p.addTabStops(new TabStop(width - document.getRightMargin(), TabAlignment.RIGHT, new DottedLine()));
                Tab tab = new Tab();
                p.add(tab);
                p.add(StringUtils.defaultString(catalog.getPageNum(), ""));
//                 添加 目录跳转
//                //Add destination
//                String destinationKey = "p" + (pdfDoc.getNumberOfPages() - 1);
//                PdfArray destinationArray = new PdfArray();
//                destinationArray.add(page.getPdfObject());
//                destinationArray.add(PdfName.XYZ);
//                destinationArray.add(new PdfNumber(0));
//                destinationArray.add(new PdfNumber(page.getMediaBox().getHeight()));
//                destinationArray.add(new PdfNumber(1));
//                pdfDoc.addNameDestination(destinationKey, destinationArray);
//                //Add TOC line with bookmark
//                Paragraph p = new Paragraph();
//                p.addTabStops(new TabStop(540, TabAlignment.RIGHT, new DottedLine()));
//                p.add(entry.getKey());
//                p.add(new Tab());
//                p.add(String.valueOf(pdfDoc.getNumberOfPages() - 1));
//                p.setProperty(Property.ACTION, PdfAction.createGoTo(destinationKey));
                document.add(p);
                _deep++;
            }
            addCatalog(document, catalog.getChildren(), index, _deep, font);
        }
    }

    /**
     * 添加页码
     * @author sunwen
     * @since 2019/12/25
     */
    private class PageIndexEvent implements IEventHandler{

        private PdfFont font;

        PageIndexEvent(PdfFont font){
            this.font = font;
        }

        @Override
        public void handleEvent(Event event) {
            PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
            PdfPage page = docEvent.getPage();
            int pageNum = docEvent.getDocument().getPageNumber(page);
            PdfCanvas canvas = new PdfCanvas(page);
            canvas.beginText();
            canvas.setFontAndSize(font, 12);
            canvas.beginMarkedContent(PdfName.Artifact);
            Rectangle rectangle = page.getPageSize();
            float width = rectangle.getWidth();
            String text = String.valueOf(pageNum);
            canvas.moveText((width - 12 * text.length()) / 2, (45 - 12) / 2f);//计算位置 底部边距内居中
            canvas.showText(text);
            canvas.endText();
            canvas.stroke();
//          canvas.addXObject(template, 0, 0);//模板占位符写入共几页
            canvas.endMarkedContent();
            canvas.release();
        }
    }

    /**
     * 设置表格换页属性
     * @author sunwen
     * @since 2019/12/25
     */
    private static void setTable(IElement element){
        if(element instanceof Table){
            Table table = (Table) element;
            int rowNum = table.getNumberOfRows();
            int colNum = table.getNumberOfColumns();
            for(int i = 0; i < rowNum; i++){
                for(int j = 0; j < colNum; j++){
                    Cell cell = table.getCell(i, j);
                    if(cell != null){
                        cell.setKeepTogether(true);
                    }
                }
            }
        }
    }

    private static class Catalog{

        private String title;//标题为空将视为纯内容
        private int pageNum;
        private List<Catalog> children;

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public int getPageNum() {
            return pageNum;
        }

        public void setPageNum(int pageNum) {
            this.pageNum = pageNum;
        }

        public List<Catalog> getChildren() {
            return children;
        }

        public Catalog nextCatalog() {
            if(children == null){
                children = new ArrayList<>();
            }
            Catalog c = new Catalog();
            children.add(c);
            return c;
        }
    }

}

html实体类

java

import java.util.List;

/**
 * html页
 * @author sunwen
 * @since 2020/12/10
 */
public class HtmlSheetDto {
    public enum Type{
        PDF, WORD
    }

    private String title;//页面标题(标题为空将视为纯内容,不计入目录)
    private Boolean showtitle;//是否显示标题(内容中不显示该标题,和是否计入目录无关)
    private Boolean newpage;//当前页内容是否需要另起一页
    private String pagesize; //页面大小 例如 A4
    private boolean rotate; //是否旋转90度
    private Integer[] margins; //页边距单位像素
    private String image;//当有图片时图片排在html前面,标题为图片标题(默认居中黑体三号字体,且不计入文档标题序列)
    private String html;//


    private List<HtmlSheetDto> children;//子页面可不设置pagerSize、rotate、margins参照父页面
}

ExcelUtil

java

import com.commnetsoft.commons.utils.UUIDUtils;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.exception.MicroRuntimeException;
import com.commnetsoft.file.converter.AbstractFileConverter;
import org.apache.poi.hssf.converter.ExcelToHtmlConverter;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @ClassName ExcelUtil
 * @Author wujs
 * @Date 2020/9/24 10:49 
 * @Version 1.0
 */
public class ExcelUtil {

    /**
     * excel转txt
     *
     * @param inputStream
     * @param targetType
     * @param sourceType
     * @return java.io.File
     * @author wujs
     * @date 2020/9/28 18:25
     */
    public static File excelToTxt(InputStream inputStream, String targetType, String sourceType) throws Exception {
        File tempDirPath = new File(System.getProperty("java.io.tmpdir"));
        File pdfFile = new File(tempDirPath, UUIDUtils.generate() + "." + targetType);
        Path pdfPath = Paths.get(pdfFile.getPath());
        //创建Excel工作薄
        Workbook work = WorkbookFactory.create(inputStream);
        work.createCellStyle();
        BufferedWriter bufferedWriter = java.nio.file.Files.newBufferedWriter(pdfPath, Charset.forName("UTF-8"));
        if (null == work) {
            throw new Exception("创建Excel工作薄为空!");
        }
        Sheet sheet = null;
        Row row = null;

        for (int i = 0; i < work.getNumberOfSheets(); i++) {
            sheet = work.getSheetAt(i);
            if (sheet == null) {
                continue;
            }
            int hf = sheet.getFirstRowNum();
            int hl = sheet.getLastRowNum();
            if (hf > hl) {
                continue;
            }
            for (int j = hf; j <= hl; j++) {
                row = sheet.getRow(j);
                if (row == null) {
                    continue;
                }
                StringBuilder strb = new StringBuilder();
                for (int y = row.getFirstCellNum(); y < row.getLastCellNum(); y++) {
                    String s = excelTime(row.getCell(y));
                    if (y == row.getLastCellNum() - 1) {
                        strb.append(s + "\n");
                    } else {
                        strb.append(s + "\t");
                    }
                }
                bufferedWriter.append(strb.toString());
                strb = null;
            }
        }
        work.close();
        bufferedWriter.close();
        inputStream.close();
        return pdfFile;
    }


    public static String excelTime(Cell cell) {
        String guarantee_time = "";
        try {
            if (DateUtil.isCellDateFormatted(cell)) {
                //用于转化为日期格式
                Date d = cell.getDateCellValue();
                DateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                guarantee_time = formater.format(d);
                return guarantee_time;
            }
        } catch (Exception e) {
        }
        return cell == null ? guarantee_time : cell.toString();
    }


    /**
     * excel转pdf
     *
     * @param inputStream
     * @param targetType
     * @param sourceType
     * @return java.io.File
     * @author wujs
     * @date 2020/9/28 18:03
     */
    public static File excelTopdf(InputStream inputStream, String targetType, String sourceType) throws IOException {
//        //设置页面大小
//        Rectangle rectPageSize = new Rectangle(PageSize.A4);// 定义A4页面大小
//        rectPageSize = rectPageSize.rotate(); //横版
//        Document document = new Document(rectPageSize, -80, -80, 50, 0); //边距
//        File tempDirPath = new File(System.getProperty("java.io.tmpdir"));
//        File pdfFile = new File(tempDirPath, UUIDUtils.generate() + "." + targetType);
//        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(pdfFile));
//
//        //字体设置
//        BaseFont bf = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
//
//        //创建Font对象,将基础字体对象,字体大小,字体风格
//        Font font = new Font(bf, 13, Font.NORMAL);
//        int rowNum = 0;
//        int colNum = 0;
//
//        Workbook work = WorkbookFactory.create(inputStream);
//        Sheet sheet = null;
//        Row row = null;
//        Cell cell = null;
//        try {
//            sheet = work.getSheetAt(0);
//            int column = sheet.getRow(0).getLastCellNum();
//            int rows = sheet.getPhysicalNumberOfRows();
//
//            //下面是找出表格中的空行和空列
//            List nullCol = new ArrayList<>();
//            List nullRow = new ArrayList<>();
//            for (int j = 0; j < column; j++) {
//                int nullColNum = 0;
//                for (int i = 0; i < rows; i++) {
//                    row = sheet.getRow(i);
//                    if (null == row) {
//                        continue;
//                    }
//                    cell = row.getCell(j);
//                    if (cell == null || (cell.getCellType() == CellType.STRING) && "".equals(cell.getStringCellValue())) {
//                        nullColNum++;
//                    }
//                }
//                if (nullColNum == rows) {
//                    nullCol.add(j);
//                }
//            }
//
//            for (int i = 0; i < rows; i++) {
//                int nullRowNum = 0;
//                for (int j = 0; j < column; j++) {
//                    row = sheet.getRow(i);
//                    if (null == row) {
//                        continue;
//                    }
//                    cell = row.getCell(j);
//                    if (cell == null || (cell.getCellType() == CellType.STRING) && "".equals(cell.getStringCellValue())) {
//                        nullRowNum++;
//                    }
//                }
//                if (nullRowNum == column) {
//                    nullRow.add(i);
//                }
//            }
//            PdfPTable table = new PdfPTable(column - sheet.getRow(0).getFirstCellNum());
//            List<CellRangeAddress> ranges = sheet.getMergedRegions();
//
//            PdfPCell cell1 = null;
//            String str = null;
//            for (int i = sheet.getFirstRowNum(); i < rows; i++) {
//                if (nullRow.contains(i)) { //如果这一行是空行,这跳过这一行
//                    continue;
//                }
//                for (int j = sheet.getRow(0).getFirstCellNum(); j < column; j++) {
//                    if (nullCol.contains(j)) { //如果这一列是空列,则跳过这一列
//                        continue;
//                    }
//                    boolean flag = true;
//                    row = sheet.getRow(i);
//                    if (null == row) {
//                        continue;
//                    }
//                    cell = row.getCell(j);
//                    if (null == cell) {
//                        continue;
//                    }
//                    if (cell.getCellType() == CellType.NUMERIC) {
//                        str = cell.getNumericCellValue() + "";
//                    } else {
//                        str = cell.getStringCellValue();
//                    }
//                    for (CellRangeAddress range : ranges) { //合并的单元格判断和处理
//                        if (j >= range.getFirstColumn() && j <= range.getLastColumn() && i >= range.getFirstRow()
//                                && i <= range.getLastRow()) {
//                            if (str == null || "".equals(str)) {
//                                flag = false;
//                                break;
//                            }
//                            rowNum = range.getLastRow() - range.getFirstRow() + 1;
//                            colNum = range.getLastColumn() - range.getFirstColumn() + 1;
//                            cell1 = mergeCell(str, font, rowNum, colNum);
//                            table.addCell(cell1);
//                            flag = false;
//                            break;
//                        }
//                    }
//                    if (flag) {
//                        table.addCell(getPDFCell(str, font));
//                    }
//                }
//            }
//            document.open();
//            document.add(table);
//            document.close();
//            return pdfFile;
//        } catch (Exception e) {
//            e.printStackTrace();
//        } finally {
//            inputStream.close();
//            writer.close();
//        }
//        return null;
        throw new MicroRuntimeException(CommonError.unimplement);
    }


//    public static PdfPCell mergeCell(String str, Font font, int i, int j) {
//
//        PdfPCell cell = new PdfPCell(new Paragraph(str, font));
//        cell.setMinimumHeight(25);
//        cell.setHorizontalAlignment(Element.ALIGN_CENTER);
//        cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
//        cell.setRowspan(i);
//        cell.setColspan(j);
//
//        return cell;
//    }
//
//    public static PdfPCell getPDFCell(String string, Font font) {
//        //创建单元格对象,将内容与字体放入段落中作为单元格内容
//        PdfPCell cell = new PdfPCell(new Paragraph(string, font));
//        cell.setHorizontalAlignment(Element.ALIGN_CENTER);
//        cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
//
//        //设置最小单元格高度
//        cell.setMinimumHeight(25);
//        return cell;
//    }

    public static File excelToWord(InputStream inputStream, String targetType, String sourceType) {
        ByteArrayOutputStream outStream = null;
        try {
            HSSFWorkbook excelBook = new HSSFWorkbook();
            //判断Excel文件将07+版本转换为03版本
            if (AbstractFileConverter.FILE_TYPE_XLS.equals(sourceType)) {  //Excel 2003
                excelBook = new HSSFWorkbook(inputStream);
            } else if (AbstractFileConverter.FILE_TYPE_XLSX.equals(sourceType)) {  // Excel 2007/2010
                Transform xls = new Transform();
                XSSFWorkbook workbookOld = new XSSFWorkbook(inputStream);
                xls.transformXSSF(workbookOld, excelBook);
            }

            ExcelToHtmlConverter excelToHtmlConverter = new ExcelToHtmlConverter(DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument());
            //去掉Excel头行
            excelToHtmlConverter.setOutputColumnHeaders(false);
            //去掉Excel行号
            excelToHtmlConverter.setOutputRowNumbers(false);
            excelToHtmlConverter.processWorkbook(excelBook);
            org.w3c.dom.Document document = excelToHtmlConverter.getDocument();
            outStream = new ByteArrayOutputStream();
            DOMSource domSource = new DOMSource(document);
            StreamResult streamResult = new StreamResult(outStream);
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer serializer = tf.newTransformer();

            serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            serializer.setOutputProperty(OutputKeys.INDENT, "yes");
            serializer.setOutputProperty(OutputKeys.METHOD, "html");

            serializer.transform(domSource, streamResult);
            //Excel转换成Html
            String content = new String(outStream.toByteArray());
            return writeWordFile(content, targetType);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                outStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public static File writeWordFile(String content, String targetType) throws Exception {

        ByteArrayInputStream byteArrayInputStream = null;
        FileOutputStream outputStream = null;
        //根据实际情况写路径
        try {
            File tempDirPath = new File(System.getProperty("java.io.tmpdir"));
            File wordFile = new File(tempDirPath, UUIDUtils.generate() + "." + targetType);
            byte[] contentBytes = content.getBytes();
            byteArrayInputStream = new ByteArrayInputStream(contentBytes);
            POIFSFileSystem poifs = new POIFSFileSystem();
            DirectoryEntry directory = poifs.getRoot();
            DocumentEntry documentEntry = directory.createDocument("WordDocument", byteArrayInputStream);
            outputStream = new FileOutputStream(wordFile);
            poifs.writeFilesystem(outputStream);
            byteArrayInputStream.close();
            outputStream.close();
            return wordFile;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (outputStream != null) outputStream.close();

            if (byteArrayInputStream != null) byteArrayInputStream.close();
        }
        return null;

    }

    public String readFile(String filename) throws Exception {
        StringBuffer buffer = new StringBuffer("");

        BufferedReader br = null;

        try {
            br = new BufferedReader(new FileReader(filename));

            buffer = new StringBuffer();

            while (br.ready())

                buffer.append((char) br.read());

        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            if (br != null) br.close();

        }

        return buffer.toString();

    }
}

ChineseSplitCharaters

java

import com.itextpdf.io.font.otf.Glyph;
import com.itextpdf.io.font.otf.GlyphLine;
import com.itextpdf.layout.splitting.ISplitCharacters;

/**
 * IText PDF中文换行规则
 * @author sunwen
 * @since 2020/12/18
 */
public class ChineseSplitCharaters implements ISplitCharacters {

    /**
     * An instance of the chinese SplitCharacter.
     */
    public static final ChineseSplitCharaters instance = new ChineseSplitCharaters();

    // line of text cannot start or end with this character
    static final char u2060 = '\u2060'; // - ZERO WIDTH NO BREAK SPACE

    // a line of text cannot start with any following characters in
    // NOT_BEGIN_CHARACTERS[]
    static final char u30fb = '\u30fb'; // ・ - KATAKANA MIDDLE DOT
    static final char u2022 = '\u2022'; // • - BLACK SMALL CIRCLE (BULLET)
    static final char uff65 = '\uff65'; // ・ - HALFWIDTH KATAKANA MIDDLE DOT
    static final char u300d = '\u300d'; // 」 - RIGHT CORNER BRACKET
    static final char uff09 = '\uff09'; // ) - FULLWIDTH RIGHT PARENTHESIS
    static final char u0021 = '\u0021'; // ! - EXCLAMATION MARK
    static final char u0025 = '\u0025'; // % - PERCENT SIGN
    static final char u0029 = '\u0029'; // ) - RIGHT PARENTHESIS
    static final char u002c = '\u002c'; // , - COMMA
    static final char u002e = '\u002e'; // . - FULL STOP
    static final char u003f = '\u003f'; // ? - QUESTION MARK
    static final char u005d = '\u005d'; // ] - RIGHT SQUARE BRACKET
    static final char u007d = '\u007d'; // } - RIGHT CURLY BRACKET
    static final char uff61 = '\uff61'; // 。 - HALFWIDTH IDEOGRAPHIC FULL STOP

    static final char uff70 = '\uff70'; // ー - HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK
    static final char uff9e = '\uff9e'; // ゙ - HALFWIDTH KATAKANA VOICED SOUND MARK
    static final char uff9f = '\uff9f'; // ゚ - HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
    static final char u3001 = '\u3001'; // 、 - IDEOGRAPHIC COMMA
    static final char u3002 = '\u3002'; // 。 - IDEOGRAPHIC FULL STOP
    static final char uff0c = '\uff0c'; // , - FULLWIDTH COMMA
    static final char uff0e = '\uff0e'; // . - FULLWIDTH FULL STOP
    static final char uff1a = '\uff1a'; // : - FULLWIDTH COLON
    static final char uff1b = '\uff1b'; // ; - FULLWIDTH SEMICOLON
    static final char uff1f = '\uff1f'; // ? - FULLWIDTH QUESTION MARK
    static final char uff01 = '\uff01'; // ! - FULLWIDTH EXCLAMATION MARK
    static final char u309b = '\u309b'; // ゛ - KATAKANA-HIRAGANA VOICED SOUND MARK
    static final char u309c = '\u309c'; // ゜ - KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
    static final char u30fd = '\u30fd'; // ヽ - KATAKANA ITERATION MARK

    static final char u2019 = '\u2019'; // ’ - RIGHT SINGLE QUOTATION MARK
    static final char u201d = '\u201d'; // ” - RIGHT DOUBLE QUOTATION MARK
    static final char u3015 = '\u3015'; // 〕 - RIGHT TORTOISE SHELL BRACKET
    static final char uff3d = '\uff3d'; // ] - FULLWIDTH RIGHT SQUARE BRACKET
    static final char uff5d = '\uff5d'; // } - FULLWIDTH RIGHT CURLY BRACKET
    static final char u3009 = '\u3009'; // 〉 - RIGHT ANGLE BRACKET
    static final char u300b = '\u300b'; // 》 - RIGHT DOUBLE ANGLE BRACKET
    static final char u300f = '\u300f'; // 』 - RIGHT WHITE CORNER BRACKET
    static final char u3011 = '\u3011'; // 】 - RIGHT BLACK LENTICULAR BRACKET
    static final char u00b0 = '\u00b0'; // ° - DEGREE SIGN
    static final char u2032 = '\u2032'; // ′ - PRIME
    static final char u2033 = '\u2033'; // ″ - DOUBLE PRIME

    static final char[] NOT_BEGIN_CHARACTERS = new char[] { u30fb, u2022, uff65, u300d, uff09, u0021, u0025, u0029,
            u002c, u002e, u003f, u005d, u007d, uff61, uff70, uff9e, uff9f, u3001, u3002, uff0c, uff0e, uff1a, uff1b,
            uff1f, uff01, u309b, u309c, u30fd, u2019, u201d, u3015, uff3d, uff5d, u3009, u300b, u300f, u3011, u00b0,
            u2032, u2033, u2060 };

    // a line of text cannot end with any following characters in
    // NOT_ENDING_CHARACTERS[]
    static final char u0024 = '\u0024'; // $ - DOLLAR SIGN
    static final char u0028 = '\u0028'; // ( - LEFT PARENTHESIS
    static final char u005b = '\u005b'; // [ - LEFT SQUARE BRACKET
    static final char u007b = '\u007b'; // { - LEFT CURLY BRACKET
    static final char u00a3 = '\u00a3'; // £ - POUND SIGN
    static final char u00a5 = '\u00a5'; // ¥ - YEN SIGN
    static final char u201c = '\u201c'; // “ - LEFT DOUBLE QUOTATION MARK
    static final char u2018 = '\u2018'; // ‘ - LEFT SINGLE QUOTATION MARK
    static final char u300a = '\u300a'; // 《 - LEFT DOUBLE ANGLE BRACKET
    static final char u3008 = '\u3008'; // 〈 - LEFT ANGLE BRACKET
    static final char u300c = '\u300c'; // 「 - LEFT CORNER BRACKET
    static final char u300e = '\u300e'; // 『 - LEFT WHITE CORNER BRACKET
    static final char u3010 = '\u3010'; // 【 - LEFT BLACK LENTICULAR BRACKET
    static final char u3014 = '\u3014'; // 〔 - LEFT TORTOISE SHELL BRACKET
    static final char uff62 = '\uff62'; // 「 - HALFWIDTH LEFT CORNER BRACKET
    static final char uff08 = '\uff08'; // ( - FULLWIDTH LEFT PARENTHESIS
    static final char uff3b = '\uff3b'; // [ - FULLWIDTH LEFT SQUARE BRACKET
    static final char uff5b = '\uff5b'; // { - FULLWIDTH LEFT CURLY BRACKET
    static final char uffe5 = '\uffe5'; // ¥ - FULLWIDTH YEN SIGN
    static final char uff04 = '\uff04'; // $ - FULLWIDTH DOLLAR SIGN

    static final char[] NOT_ENDING_CHARACTERS = new char[] { u0024, u0028, u005b, u007b, u00a3, u00a5, u201c, u2018,
            u3008, u300a, u300c, u300e, u3010, u3014, uff62, uff08, uff3b, uff5b, uffe5, uff04, u2060 };

    @Override
    public boolean isSplitCharacter(GlyphLine text, int glyphPos) {
        if (!text.get(glyphPos).hasValidUnicode()) {
            return false;
        }
        Glyph glyph = text.get(glyphPos);
        char charCode = glyph.getUnicodeChars()[0];
        //Check if a hyphen proceeds a digit to denote negative value
        if ((glyphPos == 0) && (charCode == '-') && (text.size() - 1 > glyphPos) && (isADigitChar(text, glyphPos + 1))) {
            return false;
        }
        int next = glyphPos + 1;
        if (next < text.size()) {//下一个是标点,不能分隔
            Glyph nextGlyph = text.get(next);
            char charNext = nextGlyph.getUnicodeChars()[0];
            for (char not_begin_character : NOT_BEGIN_CHARACTERS) {
                if (charNext == not_begin_character) {
                    return false;
                }
            }
        }
        for (char not_ending_character : NOT_ENDING_CHARACTERS) {
            if (charCode == not_ending_character) {
                return false;
            }
        }
        for (char not_ending_character : NOT_BEGIN_CHARACTERS) {
            if (charCode == not_ending_character) {
                return true;
            }
        }
        return (charCode <= ' ' || charCode == '-' || charCode == '\u2010'
                || (charCode >= 0x2002 && charCode <= 0x200b)
                || (charCode >= 0x2e80 && charCode < 0xd7a0)
                || (charCode >= 0xf900 && charCode < 0xfb00)
                || (charCode >= 0xfe30 && charCode < 0xfe50)
                || (charCode >= 0xff61 && charCode < 0xffa0));
    }

    private boolean isADigitChar(GlyphLine text, int glyphPos) {
        return Character.isDigit(text.get(glyphPos).getChars()[0]);
    }
}

DocxHelper

java

import com.aspose.words.SaveFormat;
import com.commnetsoft.commons.utils.UUIDUtils;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.exception.MicroRuntimeException;
import com.commnetsoft.file.model.HtmlSheetDto;
import com.itextpdf.io.codec.Base64;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.docx4j.Docx4J;
import org.docx4j.Docx4jProperties;
import org.docx4j.TraversalUtil;
import org.docx4j.convert.in.xhtml.XHTMLImporterImpl;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.finders.RangeFinder;
import org.docx4j.model.structure.PageDimensions;
import org.docx4j.model.structure.PageSizePaper;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.openpackaging.parts.WordprocessingML.NumberingDefinitionsPart;
import org.docx4j.wml.*;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.*;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import java.io.*;
import java.math.BigInteger;
import java.util.*;

/**
 * @ClassName WordHelper
 * @Author wuzm
 * @Date 2020/12/28 14:46
 * @Version 1.0
 */
@Component
public class DocxHelper {
    private static final Logger log = LoggerFactory.getLogger(DocxHelper.class);
    private static final ObjectFactory factory = new ObjectFactory();
    private static final int[] DEFAULT_MARGIN = {1440, 1800, 1440, 1800};

    public void convertBook(HtmlSheetDto htmlSheetDto, OutputStream out, boolean autoOrdinal) throws Exception {
        boolean needCatalog = isNeedCatalog(htmlSheetDto, 0);

        WordprocessingMLPackage wpmlPackage = createWPMLPackage(htmlSheetDto.getPagesize(), BooleanUtils.isTrue(htmlSheetDto.isRotate()), htmlSheetDto.getMargins(),true);
        addBookSheet(wpmlPackage, htmlSheetDto, 0, 1, needCatalog, autoOrdinal);
        if(needCatalog){
            File file = new File(System.getProperty("java.io.tmpdir"), UUIDUtils.generate());
            wpmlPackage.save(new FileOutputStream(file));
            //更新目录
            com.aspose.words.Document doc = new com.aspose.words.Document(new FileInputStream(file));
            doc.updateFields();
            //如果需要自动编号,目录生成之后再删除标题上的编号
            if(autoOrdinal){
                doc.save(new FileOutputStream(file), SaveFormat.DOCX);
                if (!removeTitleNum(file, out)) {
                    doc.save(out, SaveFormat.DOCX);
                }
            }else{
                doc.save(out, SaveFormat.DOCX);
            }
            file.delete();
        }else{
            wpmlPackage.save(out);
        }
    }

    public void convertBook(HtmlSheetDto htmlSheetDto, File file, boolean autoOrdinal) throws Exception {
        WordprocessingMLPackage wpmlPackage = createWPMLPackage(htmlSheetDto.getPagesize(), BooleanUtils.isTrue(htmlSheetDto.isRotate()), htmlSheetDto.getMargins(), true);
        //添加目录
        boolean needCatalog = isNeedCatalog(htmlSheetDto, 0);
        addBookSheet(wpmlPackage, htmlSheetDto, 0, 1, needCatalog, autoOrdinal);
        wpmlPackage.save(new FileOutputStream(file));
        if (needCatalog) {
            //更新目录
            com.aspose.words.Document doc = new com.aspose.words.Document(new FileInputStream(file));
            doc.updateFields();
            doc.save(new FileOutputStream(file), SaveFormat.DOCX);
            if(autoOrdinal){
                removeTitleNum(file);
            }
        }
    }

    /**
     * 移除文件标题上的序号,并将结果写出到输出流中
     * @param file 生成目录后的文件
     * @param out  输出流
     * @return boolean 是否有内容被移除了
     * @author wuzm
     * @date 2021/1/14
     */
    private boolean removeTitleNum(File file, OutputStream out) throws Docx4JException, FileNotFoundException {
        WordprocessingMLPackage loadFile = WordprocessingMLPackage.load(new FileInputStream(file));
        boolean hasRemoved = removeTitleNum(loadFile);
        if (hasRemoved) {
            Docx4J.save(loadFile, out);
        }
        return hasRemoved;
    }

    /**
     * 移除文件标题上的序号,移除后的内容覆盖原文件
     * @param file      生成目录后的文件
     * @return boolean 是否有内容被移除了
     * @author wuzm
     * @date 2021/1/14
     */
    private boolean removeTitleNum(File file) throws Docx4JException, FileNotFoundException {
        WordprocessingMLPackage loadFile = WordprocessingMLPackage.load(new FileInputStream(file));
        boolean hasRemoved = removeTitleNum(loadFile);
        if (hasRemoved) {
            Docx4J.save(loadFile, new FileOutputStream(file));
        }
        return hasRemoved;
    }

    /**
     * 移除文件标题上的序号
     * @param loadFile docx4j加载后的内容
     * @return boolean 是否有内容被移除了
     * @author wuzm
     * @date 2021/1/14
     */
    private boolean removeTitleNum(WordprocessingMLPackage loadFile) {
        boolean hasRemoved = false;
        List<Object> contents = loadFile.getMainDocumentPart().getContent();
        RangeFinder rt = new RangeFinder("CTBookmark", "CTMarkupRange");
        new TraversalUtil(contents, rt);
        for (CTBookmark bm : rt.getStarts()) {
            Object parent = bm.getParent();
            if (!(parent instanceof P)) {
                continue;
            }
            P parentP = (P) parent;
            PPr pPr = parentP.getPPr();
            if (null == pPr) {
                continue;
            }
            PPrBase.NumPr numPr = pPr.getNumPr();
            if (null == numPr) {
                continue;
            }
            //移除NumPr
            pPr.setNumPr(null);
            //缩进处理
            PPrBase.Ind ind = pPr.getInd();
            if (null == ind) {
                continue;
            }
            BigInteger left = ind.getLeft();
            BigInteger hanging = ind.getHanging();
            if (null != left && (!BigInteger.ZERO.equals(left)) && (left.equals(hanging))) {
                pPr.setInd(null);
            }
            if (!hasRemoved) {
                hasRemoved = true;
            }else{
                removeCatalogTab(loadFile);
            }
        }
        return hasRemoved;
    }

    /**
     * 移除目录中的tab
     * @param loadFile 生成目录后的文件
     * @author wuzm
     * @date 2021/1/15
     */
    private void removeCatalogTab(WordprocessingMLPackage loadFile) {
        List<Object> contents = loadFile.getMainDocumentPart().getContent();
        for (Object content : contents) {
            if(!(content instanceof SdtBlock)){
                continue;
            }

            SdtBlock sdtBlock = (SdtBlock) content;
            SdtContent sdtContent = sdtBlock.getSdtContent();
            List<Object> sdtContents = sdtContent.getContent();
            for (Object sdtObjContent : sdtContents) {
                if(!(sdtObjContent instanceof P)){
                    continue;
                }
                P p = (P) sdtObjContent;
                boolean isBreak = false;
                List<Object> pContentObjs = p.getContent();
                Iterator<Object> iterator = pContentObjs.iterator();
                while (iterator.hasNext()) {
                    if(isBreak){
                        break;
                    }
                    Object pContentObj = iterator.next();
                    if(!(pContentObj instanceof R)){
                        continue;
                    }
                    R r = (R) pContentObj;
                    List<Object> rContentObjs = r.getContent();
                    JAXBElement rFristContent = (JAXBElement)rContentObjs.get(0);
                    if(StringUtils.equals("tab",rFristContent.getName().getLocalPart())){
                        //含有noProof属性的将当前元素移除并跳出循环
                        RPr rPr = r.getRPr();
                        if(null != rPr.getNoProof()){
                            iterator.remove();
                            isBreak = true;
                        }
                    }
                }
            }
            break;
        }
    }

    /**
     * 判断是否需要添加目录
     * @param htmlSheetDto html原内容
     * @return boolean
     * @author wuzm
     * @date 2021/1/12
     */
    private boolean isNeedCatalog(HtmlSheetDto htmlSheetDto,int level){
        if (level > 0 && StringUtils.isNotEmpty(htmlSheetDto.getTitle()) && StringUtils.isEmpty(htmlSheetDto.getImage())) {
            return true;
        }
        level += 1;
        if(CollectionUtils.isNotEmpty(htmlSheetDto.getChildren())){
            for (HtmlSheetDto childHtmlSheetDto : htmlSheetDto.getChildren()) {
                if(isNeedCatalog(childHtmlSheetDto,level)){
                    return true;
                }
            }
        }
        return false;
    }

    private int addBookSheet(WordprocessingMLPackage wpmlPackage, HtmlSheetDto htmlSheetDto, int level, int chapterCount, boolean isNeedCatalog, boolean autoOrdinal) throws Exception {
        List<Object> contents = wpmlPackage.getMainDocumentPart().getContent();

        if (level > 0 && BooleanUtils.isTrue(htmlSheetDto.getNewpage())) {
            chapterCount += 1;
            P p = createNewPage(htmlSheetDto);
            SectPr sectPr = p.getPPr().getSectPr();
            //第章节插入页码从1开始
            if (chapterCount == 3) {
                PageNumHelper.addPageNumFooter(wpmlPackage, sectPr, 0);
                //设置开始页码
                sectPr.setPgNumType(getPageNum(1));
            }
            contents.add(p);
        }

        addSheet(wpmlPackage, htmlSheetDto, level, true, autoOrdinal);
        //添加目录
        if (level == 0 && isNeedCatalog) {
            //先添加分页隔离封面
            P p = createNewPage(htmlSheetDto);
            contents.add(p);
            //插入空目录
            Docx4jProperties.setProperty("docx4j.toc.TocStyles.xml", "docx/TocStyles.xml");
            TocGenerator tocGenerator = new TocGenerator(wpmlPackage);
            int index = wpmlPackage.getMainDocumentPart().getContent().size();
            tocGenerator.generateToc(index, "TOC \\o \"1-3\" \\h \\z \\u ", false);
        }

        level += 1;
        //递归添加子页面
        if (CollectionUtils.isNotEmpty(htmlSheetDto.getChildren())) {
            for (HtmlSheetDto childSheet : htmlSheetDto.getChildren()) {
                chapterCount = addBookSheet(wpmlPackage, childSheet, level, chapterCount, isNeedCatalog, autoOrdinal);
            }
        }
        return chapterCount;
    }

    private void addSheet(WordprocessingMLPackage wpmlPackage, HtmlSheetDto htmlSheetDto, int level,boolean isBook, boolean autoOrdinal) throws Exception {
        MainDocumentPart mainDocumentPart = wpmlPackage.getMainDocumentPart();
        List<Object> contents = mainDocumentPart.getContent();
        boolean showTitle = BooleanUtils.isTrue(htmlSheetDto.getShowtitle());
        String title = htmlSheetDto.getTitle();
        String image = htmlSheetDto.getImage();
        //判断是否需要将转换后的第一个内容标记为目录,只对小册子生效
        boolean relateFirstParagraphToCatalog = false;
        //添加标题(title不为空,image为空)
        if (level > 0 && StringUtils.isNotEmpty(title) && StringUtils.isEmpty(image)) {
            //如果显示目录,则创建对应层级的目录,如果不显示,则将转换后的第一段设置为目录(第一段必须是标题字段,如果不是则手动创建一个)
            if(showTitle){
                addTitle(mainDocumentPart, level, title, autoOrdinal);
            }else if(isBook){
                relateFirstParagraphToCatalog = true;
            }
        }

        //添加图片
        if (StringUtils.isNotEmpty(image)) {
            byte[] bytes;
            String[] strParts = image.split(",");
            if (strParts.length == 2 && ("data:image/gif;base64".equals(strParts[0]) || "data:image/png;base64".equals(strParts[0]) || "data:image/jpeg;base64".equals(strParts[0]))) {
                bytes = Base64.decode(strParts[1]);
            } else {
                throw new MicroRuntimeException(CommonError.illegal_args, "不支持的图片格式");
            }

            //添加图片标题
            if (showTitle && StringUtils.isNotEmpty(htmlSheetDto.getTitle())) {
                P text = createText(JcEnumeration.CENTER, htmlSheetDto.getTitle(),"黑体",32);
                PPr pPr = text.getPPr();
                pPr.setKeepNext(new BooleanDefaultTrue());
                mainDocumentPart.getContent().add(text);
            }

            String filenameHint = htmlSheetDto.getTitle();
            String altText = htmlSheetDto.getTitle();
            int id1 = RandomUtils.nextInt(500, Integer.MAX_VALUE);
            int id2 = RandomUtils.nextInt(500, Integer.MAX_VALUE);
            P p = newImage(wpmlPackage, bytes, filenameHint, altText, id1, id2);
            contents.add(p);
        }

        String html = htmlSheetDto.getHtml();
        if(StringUtils.isNotEmpty(html)){
            List<Object> convertContents = new XHTMLImporterImpl(wpmlPackage).convert(htmlToXhtml(html), null, getExtentStyle(htmlSheetDto));
            //设置转换后的第一段落中内容是文本的设置成目录关联内容
            if(relateFirstParagraphToCatalog){
                markAsCatalogOutLevel(mainDocumentPart, convertContents, level, title, autoOrdinal);
            }
            contents.addAll(convertContents);
        }
    }

    /**
     * 将第一个段落设置为目录外链接
     * 层级大于6的设置成6
     * @param mainDocumentPart 文档内容
     * @param convertContents 转换后的内容
     * @param level 目录层级
     * @param title 标题
     * @author wuzm
     * @date 2021/1/13
     */
    private void markAsCatalogOutLevel(MainDocumentPart mainDocumentPart, List<Object> convertContents, Integer level, String title, boolean autoOrdinal) {
        if (null == level || level == 0) {
            return;
        }

        if (CollectionUtils.isEmpty(convertContents)) {
            return;
        }
        //第一个段落内容如果不是段落则添加标题
        Object firstContent = convertContents.get(0);
        if(!(firstContent instanceof P)){
            addTitle(mainDocumentPart, level, title, autoOrdinal);
        }else{
            //直接将第一段设置成目录外链
            P firstP = (P) firstContent;
            setOutLevel(firstP, level);
            //添加标题
            if(autoOrdinal){
                addOrdinal(level, autoOrdinal, firstP);
            }
        }
    }

    private void setOutLevel(P p,int level){
        if(null == p){
            return;
        }
        PPr pPr = p.getPPr();
        if(null == pPr){
            pPr = factory.createPPr();
            p.setPPr(pPr);
        }
        PPrBase.OutlineLvl outlineLvl = factory.createPPrBaseOutlineLvl();
        outlineLvl.setVal(BigInteger.valueOf((level-1)));
        pPr.setOutlineLvl(outlineLvl);
    }

    public void convert(HtmlSheetDto htmlSheetDto, OutputStream out) throws Exception {
        WordprocessingMLPackage wpmlPackage = createWPMLPackage(htmlSheetDto.getPagesize(), htmlSheetDto.isRotate(), htmlSheetDto.getMargins(), true);
        SectPr sectPr = wpmlPackage.getMainDocumentPart().getContents().getBody().getSectPr();
        addEachSheet(wpmlPackage,htmlSheetDto,1);

        //添加页码
        PageNumHelper.addPageNumFooter(wpmlPackage, sectPr, 1);
        wpmlPackage.save(out);
    }

    private void addEachSheet(WordprocessingMLPackage wpmlPackage, HtmlSheetDto htmlSheetDto, int level) throws Exception {
        addSheet(wpmlPackage, htmlSheetDto, level, false, false);
        level += 1;
        //递归添加子页面
        if(CollectionUtils.isNotEmpty(htmlSheetDto.getChildren())){
            for (HtmlSheetDto childSheet : htmlSheetDto.getChildren()) {
                addEachSheet(wpmlPackage, childSheet, level);
            }
        }
    }

    /**
     * 将一个html转换成docx文件,不带页码
     * @param html html源码
     * @param pageSize 页面大小
     * @param rotate 是否旋转
     * @param margins 边距
     * @param out 输出流
     * @author wuzm
     * @date 2020/12/28
     */
    public void convert(String html, String pageSize, boolean rotate, Integer[] margins, OutputStream out) throws Docx4JException, JAXBException {
        WordprocessingMLPackage wpmlPackage = createWPMLPackage(pageSize, rotate, margins,false);
        //转换html
        List<Object> convertContents = new XHTMLImporterImpl(wpmlPackage).convert(htmlToXhtml(html), null);
        wpmlPackage.getMainDocumentPart().getContent().addAll(convertContents);
        //输出
        wpmlPackage.save(out);
    }

    /**
     * html转xhtml
     * @param html 源码
     * @return java.lang.String
     * @author wuzm
     * @date 2020/12/30
     */
    private String htmlToXhtml(String html){
        if(org.apache.commons.lang3.StringUtils.isBlank(html)){
            return html;
        }
        //替换text-decoration-line为text-decoration
        html = StringUtils.replace(html, "text-decoration-line", "text-decoration");
        org.jsoup.nodes.Document doc = Jsoup.parse(html);
        removeEmptyContent(doc);
        removeBrElements(doc);
        doc.outputSettings().syntax(Document.OutputSettings.Syntax.xml).escapeMode(Entities.EscapeMode.xhtml);
        return doc.html();
    }

    /**
     * 移除空的段落
     * @author wuzm
     * @date 2021/1/29
     */
    private void removeEmptyContent(Document doc) {
        Elements bodys = doc.getElementsByTag("body");
        Iterator<Element> iterator = bodys.iterator();
        Set<Node> removeNodes = new HashSet<>();
        while (iterator.hasNext()){
            Element nextBody = iterator.next();
            List<Node> nodes = nextBody.childNodes();
            for (Node node : nodes) {
                if(isEmptyContentNode(node)){
                    removeNodes.add(node);
                }
            }
        }
        if(removeNodes.size() > 0){
            removeNodes.forEach(Node::remove);
        }
    }

    /**
     * 处理br标签换行问题
     * @author wuzm
     * @date 2021/1/8
     */
    private void removeBrElements(Document doc) {
        Elements brs = doc.getElementsByTag("br");
        Iterator<Element> iterator = brs.iterator();
        Set<Node> removeNodes = new HashSet<>();
        while (iterator.hasNext()){
            Element nextBr = iterator.next();
            Node node = nextBr.parentNode();
            List<Node> childNodes = node.childNodes();
            List<Integer> brIndexs = new ArrayList<>();
            for (int i = 0; i < childNodes.size(); i++) {
                if (StringUtils.equals(childNodes.get(i).nodeName(), "br")) {
                    brIndexs.add(i);
                }
            }

            Set<Integer> removeNodeIndexs = new HashSet<>();

            //获取最后一个br索引
            Integer lastBrIndex = brIndexs.get(brIndexs.size() - 1);
            //如果最后一个br是父元素的最后一个子元素则移除
            if(lastBrIndex == (childNodes.size()-1)){
                removeNodeIndexs.add(lastBrIndex);
            }else{
                //判断该元素后的元素是否都是空元素,如果是,则移除当前元素以及后面的其他元素
                boolean isEmpty = true;
                for (int i = lastBrIndex+1; i <childNodes.size(); i++){
                    Node childNode = childNodes.get(i);
                    if(!isEmptyTextNode(childNode)){
                        isEmpty = false;
                        break;
                    }
                }
                if(isEmpty){
                    for (int i = lastBrIndex; i <childNodes.size(); i++){
                        removeNodeIndexs.add(i);
                    }
                }
            }

            for (Integer removeNodeIndex : removeNodeIndexs) {
                removeNodes.add(childNodes.get(removeNodeIndex));
            }
        }
        for (Node removeNode : removeNodes) {
            removeNode.remove();
        }
    }

    /**
     * 判断节点是否是空的文本
     * @param node     节点
     * @return boolean
     * @author wuzm
     * @date 2021/1/25
     */
    private boolean isEmptyTextNode(Node node){
        if(null == node){
            return true;
        }

        if(node instanceof TextNode){
            TextNode textNode = (TextNode) node;
            return textNode.isBlank();
        }else{
            List<Node> nodes = node.childNodes();
            if(null == nodes || nodes.size() == 0){
                return true;
            }
            for (Node childNode : nodes) {
                if(!isEmptyTextNode(childNode)){
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 判断节点是否是空的内容,br/img不算空内容
     * @param node
     * @return boolean
     * @author wuzm
     * @date 2021/1/29
     */
    private boolean isEmptyContentNode(Node node){
        if(null == node){
            return true;
        }

        if(node instanceof TextNode){
            TextNode textNode = (TextNode) node;
            return textNode.isBlank();
        }else if(node.nodeName().equals("img")){
            return false;
        }else if(node.nodeName().equals("br")){
            return false;
        }else{
            List<Node> nodes = node.childNodes();
            if(null == nodes || nodes.size() == 0){
                return true;
            }
            for (Node childNode : nodes) {
                if(!isEmptyContentNode(childNode)){
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 判断节点是否是文本节点并且内容为空
     * @param node 被判断的节点
     * @return boolean
     * @author wuzm
     * @date 2021/1/8
     */
    private boolean isNodeEmptyContent(Node node){
        if(null == node){
            return true;
        }

        if(node instanceof TextNode){
            TextNode textNode = (TextNode) node;
            return textNode.isBlank();
        }
        return false;
    }

    /**
     * 创建新的页面
     * @param sheet 页面信息
     * @return org.docx4j.wml.P
     * @author wuzm
     * @date 2021/1/8
     */
    private P createNewPage(HtmlSheetDto sheet) {
        P p = factory.createP();
        PPr pPr = factory.createPPr();
        //创建章节
        SectPr sectPr = createSectPr(sheet);
        pPr.setSectPr(sectPr);
        p.setPPr(pPr);
        return p;
    }

    /**
     * 创建章节
     * @param sheet 页面信息
     * @return org.docx4j.wml.SectPr
     * @author wuzm
     * @date 2021/1/8
     */
    private SectPr createSectPr(HtmlSheetDto sheet){
        //获取页面大小
        PageSizePaper pageSizePaper = getPageSizePaper(sheet.getPagesize());
        //是否旋转
        boolean rotate = BooleanUtils.isTrue(sheet.isRotate());
        PageDimensions page = new PageDimensions();
        page.setPgSize(pageSizePaper, rotate);
        SectPr sectPr = factory.createSectPr();
        sectPr.setPgSz(  page.getPgSz() );
        sectPr.setPgMar( page.getPgMar() );

        //重新设置页面边距
        resetPgMar(sectPr.getPgMar(),sheet.getMargins());
        return sectPr;
    }

    /**
     * 获取页面额外信息设置成样式属性
     * @param sheet 页面属性
     * @return java.lang.String
     * @author wuzm
     * @date 2021/1/22
     */
    private String getExtentStyle(HtmlSheetDto sheet){
        SectPr sectPr = createSectPr(sheet);
        StringBuilder sb = new StringBuilder();
        //设置页面样式
        SectPr.PgSz pgSz = sectPr.getPgSz();
        if(null != pgSz){
            sb.append("@page{");
            BigInteger width = pgSz.getW();
            if(null != width && width.intValue() > 0){
                sb.append("-fs-page-width:").append(width.intValue()).append(";");
            }
            BigInteger height = pgSz.getH();
            if(null != height && height.intValue() > 0){
                sb.append("-fs-page-height:").append(height.intValue()).append(";");
            }
            STPageOrientation orient = pgSz.getOrient();
            if(null != orient && StringUtils.isNotEmpty(orient.value())){
                sb.append("-fs-page-orientation:").append(orient.value()).append(";");
            }
            SectPr.PgMar pgMar = sectPr.getPgMar();
            if(null != pgMar){
                sb.append("margin:")
                .append(pgMar.getTop()).append(" ")
                .append(pgMar.getRight()).append(" ")
                .append(pgMar.getBottom()).append(" ")
                .append(pgMar.getRight()).append(";");
            }
            sb.append("}");
            sb.append(" ");
        }
        sb.append("body{margin: 0 !important;}");
        return sb.toString();
    }

    /**
     * 创建图片
     * @param wordMLPackage 文档主体
     * @param bytes 图片字节
     * @param filenameHint 文件名
     * @param altText 文件不存在时候显示内容
     * @param id1 文件识别码1
     * @param id2 文件识别码2
     * @return org.docx4j.wml.P
     * @author wuzm
     * @date 2021/1/8
     */
    private P newImage(WordprocessingMLPackage wordMLPackage, byte[] bytes, String filenameHint, String altText, int id1, int id2) throws Exception {
        BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes);
        Inline inline = imagePart.createImageInline( filenameHint, altText, id1, id2, false);

        P p = createNewParagraph(JcEnumeration.CENTER);

        R run = factory.createR();
        p.getContent().add(run);
        Drawing drawing = factory.createDrawing();
        run.getContent().add(drawing);
        drawing.getAnchorOrInline().add(inline);

        return p;
    }

    private void addTitle(MainDocumentPart mainDocumentPart, Integer level, String title, boolean autoOrdinal) {
        P p = createTitle(mainDocumentPart, level , title, autoOrdinal);
        mainDocumentPart.getContent().add(p);
    }

    /**
     * 创建标题或者内容段落
     * @param mainDocumentPart 文档主文档
     * @param level 当前的层级,用于判断添加标题的级别,有效值为1-6, 其他的数值认为是文本内容
     * @param title 标题内容
     * @return org.docx4j.wml.P
     * @author wuzm
     * @date 2021/1/13
     */
    private P createTitle(MainDocumentPart mainDocumentPart, Integer level, String title, boolean autoOrdinal) {
        P p;
        //如果层级在1-6则添加标题,其他默认是最低等级标题
        if (null != level && level < 7 && level > 0) {
            p = mainDocumentPart.createStyledParagraphOfText("Heading" + level, title);
        } else {
            p = mainDocumentPart.createStyledParagraphOfText("" + 7, title);
        }
        addOrdinal(level, autoOrdinal, p);
        return p;
    }

    private void addOrdinal(Integer level, boolean autoOrdinal, P p) {
        //添加编号
        if (autoOrdinal && level > 0 && level < 4) {
            PPr pPr = p.getPPr();
            if (null == pPr) {
                pPr = factory.createPPr();
                p.setPPr(pPr);
            }

            PPrBase.NumPr numPr = factory.createPPrBaseNumPr();

            PPrBase.NumPr.Ilvl ilvl = factory.createPPrBaseNumPrIlvl();
            ilvl.setVal(BigInteger.valueOf((level-1)));
            numPr.setIlvl(ilvl);

            PPrBase.NumPr.NumId numId = factory.createPPrBaseNumPrNumId();
            numId.setVal(BigInteger.ONE);
            numPr.setNumId(numId);
            pPr.setNumPr(numPr);

            //设置缩进
            PPrBase.Ind ind = factory.createPPrBaseInd();
            BigInteger hanging;
            if (1 == level) {
                hanging = BigInteger.valueOf(425);
            } else if (level == 2) {
                hanging = BigInteger.valueOf(567);
            } else {
                hanging = BigInteger.valueOf(709);
            }
            ind.setHanging(hanging);
            ind.setLeft(hanging);
            ind.setLeftChars(BigInteger.ZERO);
            ind.setFirstLineChars(BigInteger.ZERO);
            pPr.setInd(ind);
        }
    }

    private void addText(MainDocumentPart mainDocumentPart,JcEnumeration jcEnumeration, String content) {
        P p = createText(jcEnumeration, content);
        mainDocumentPart.getContent().add(p);
    }

    private P createText(JcEnumeration jcEnumeration, String content) {
        P p = createNewParagraph(jcEnumeration);

        R r = factory.createR();
        Text text = factory.createText();
        text.setValue(content);
        r.getContent().add(text);
        p.getContent().add(r);
        return p;
    }

    private P createText(JcEnumeration jcEnumeration, String content, String fontType, Integer fontSize) {
        P p = createNewParagraph(jcEnumeration);

        R r = factory.createR();
        Text text = factory.createText();
        text.setValue(content);
        if(StringUtils.isNotEmpty(fontType) || null != fontSize){
            RPr rPr = factory.createRPr();
            r.setRPr(rPr);
            if(StringUtils.isNotEmpty(fontType)){
                RFonts rFonts = factory.createRFonts();
                rPr.setRFonts(rFonts);
                rFonts.setAscii(fontType);
                rFonts.setEastAsia(fontType);
                rFonts.setHint(STHint.EAST_ASIA);
            }

            if(null != fontSize){
                HpsMeasure hpsMeasure = factory.createHpsMeasure();
                hpsMeasure.setVal(BigInteger.valueOf(fontSize));
                rPr.setSz(hpsMeasure);
            }
        }
        r.getContent().add(text);
        p.getContent().add(r);
        return p;
    }

    /**
     * 创建新的的段落
     * @return org.docx4j.wml.P
     * @author wuzm
     * @date 2020/12/29
     */
    private P createNewParagraph(JcEnumeration jcEnumeration){
        P p = factory.createP();
        //设置居中属性
        PPr pPr = factory.createPPr();
        Jc jc = factory.createJc();
        jc.setVal(jcEnumeration);
        pPr.setJc(jc);
        p.setPPr(pPr);
        return p;
    }

    /**
     * 创建文档
     * @param pageSize 页面大小
     * @param rotate 是否旋转
     * @param margins 边距
     * @return org.docx4j.openpackaging.packages.WordprocessingMLPackage
     * @author wuzm
     * @date 2020/12/28
     */
    private WordprocessingMLPackage createWPMLPackage(String pageSize, boolean rotate, Integer[] margins, boolean needPageNum) throws Docx4JException, JAXBException {
        PageSizePaper pageSizePaper = getPageSizePaper(pageSize);
        Docx4jProperties.setProperty("docx4j.openpackaging.parts.WordprocessingML.NumberingDefinitionsPart.DefaultNumbering", "docx/numbering.xml");
        Docx4jProperties.setProperty("docx4j.openpackaging.parts.WordprocessingML.StyleDefinitionsPart.KnownStyles", "docx/KnownStyles.xml");
        Docx4jProperties.setProperty("docx4j.openpackaging.parts.WordprocessingML.StyleDefinitionsPart.DefaultStyles", "docx/styles.xml");
        WordprocessingMLPackage wMLPackage = WordprocessingMLPackage.createPackage(pageSizePaper, rotate);

        //自定义序号样式为层级缩进样式
        NumberingDefinitionsPart numberPart = new org.docx4j.openpackaging.parts.WordprocessingML.NumberingDefinitionsPart();
        numberPart.unmarshalDefaultNumbering();
        wMLPackage.getMainDocumentPart().addTargetPart(numberPart);

        SectPr sectPr = wMLPackage.getMainDocumentPart().getContents().getBody().getSectPr();
        SectPr.PgMar pgMar = sectPr.getPgMar();

        //重新设置页边距
        resetPgMar(pgMar, margins);
        //判断文档是否需要页码
        if(needPageNum){
            sectPr.setPgNumType(getPageNum(null));
        }
        return wMLPackage;
    }

    /**
     * 获取页面大小
     * @param pageSize 页面大小类型:A4,A3...
     * @return org.docx4j.model.structure.PageSizePaper
     * @author wuzm
     * @date 2021/1/8
     */
    private PageSizePaper getPageSizePaper(String pageSize){
        PageSizePaper pageSizePaper;
        if(StringUtils.isNotEmpty(pageSize)){
            pageSizePaper = PageSizePaper.valueOf(pageSize);
        }else{
            pageSizePaper = PageSizePaper.A4;
        }
        return pageSizePaper;
    }

    /**
     * 重新设置文档页边距
     * @param pgMar 文档布局属性
     * @param margins 新的文档布局
     * @author wuzm
     * @date 2021/1/8
     */
    private void resetPgMar(SectPr.PgMar pgMar, Integer[] margins){
        int[] mg = getPgMargin(margins);

        int top = mg[0];
        int bottom = mg[2];
        pgMar.setTop(BigInteger.valueOf(top));
        pgMar.setRight(BigInteger.valueOf(mg[1]));
        pgMar.setBottom(BigInteger.valueOf(bottom));
        pgMar.setLeft(BigInteger.valueOf(mg[3]));

        long header = footerHeaderMargin(top);
        pgMar.setHeader(BigInteger.valueOf(header));

        long footer = footerHeaderMargin(bottom);
        pgMar.setFooter(BigInteger.valueOf(footer));
        pgMar.setGutter(BigInteger.ZERO);
    }

    private int[] getPgMargin(Integer[] margins){
        int[] mg = {DEFAULT_MARGIN[0], DEFAULT_MARGIN[1], DEFAULT_MARGIN[2], DEFAULT_MARGIN[3]};
        if(null != margins){
            for (int i = 0; i < mg.length; i++) {
                Integer m = margins[i];
                if(m == null || m < 0){
                    continue;
                }
                mg[i] = margins[i] * 15;
            }
        }
        return mg;
    }

    private int footerHeaderMargin(int height){
        int marging;
        if(height > 480 ){
            marging = height - 480;
        }else {
            marging = 0;
        }
        return marging;
    }

    /**
     * 创建页码
     * @param startNum 页码开始的数
     * @return org.docx4j.wml.CTPageNumber
     * @author wuzm
     * @date 2021/1/8
     */
    private CTPageNumber getPageNum(Integer startNum){
        CTPageNumber pageNumber = factory.createCTPageNumber();
        pageNumber.setFmt(NumberFormat.DECIMAL);
        if(null != startNum){
            pageNumber.setStart(BigInteger.valueOf(startNum));
        }
        return pageNumber;
    }
}

PageNumHelper

java

import org.docx4j.XmlUtils;
import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.FooterPart;
import org.docx4j.relationships.Relationship;
import org.docx4j.wml.*;

import javax.xml.bind.JAXBException;

/**
 * @ClassName PageNumHelper
 * @Author wuzm
 * @Date 2021/1/5 9:26
 * @Version 1.0
 */
public class PageNumHelper {
    private static String getPageNumFooterXml(int fromPageNum){
        return "<w:p  xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" >\n" +
                "   <w:pPr>\n" +
                "       <w:pStyle w:val=\"8\"/>\n" +
                "       <w:jc w:val=\"center\"/>"+
                "   </w:pPr>\n" +
                "   <w:r>\n" +
                "       <w:fldChar w:fldCharType=\"begin\"/>\n" +
                "   </w:r>\n" +
                "   <w:r>\n" +
                "       <w:instrText xml:space=\"preserve\"> PAGE  \\* MERGEFORMAT </w:instrText>\n" +
                "   </w:r>\n" +
                "   <w:r>\n" +
                "       <w:fldChar w:fldCharType=\"separate\"/>\n" +
                "   </w:r>\n" +
                "   <w:r>\n" +
                "       <w:t>"+ fromPageNum +"</w:t>\n" +
                "   </w:r>\n" +
                "   <w:r>\n" +
                "       <w:fldChar w:fldCharType=\"end\"/>\n" +
                "   </w:r>\n" +
                "</w:p>";
    }

    //创建页脚模块
    public static void addPageNumFooter(WordprocessingMLPackage wordMLPackage,SectPr sectPr, Integer fromPageNum) throws JAXBException, InvalidFormatException {
        Relationship relationship = createFooterPart(wordMLPackage,fromPageNum);
        //创建引用
        addReferences(sectPr, relationship);
    }

    public static Relationship createFooterPart(WordprocessingMLPackage wordMLPackage, Integer fromPageNum) throws InvalidFormatException, JAXBException {
        FooterPart footerPart = new FooterPart();
        Relationship relationship =  wordMLPackage.getMainDocumentPart().addTargetPart(footerPart);
        footerPart.setJaxbElement(getFtr(fromPageNum));
        return relationship;
    }

    private static Ftr getFtr(Integer fromPageNum) throws JAXBException {
        Ftr ftr = Context.getWmlObjectFactory().createFtr();
        P p;
        if(null == fromPageNum){
            p = Context.getWmlObjectFactory().createP();
            PPr pPr = Context.getWmlObjectFactory().createPPr();
            PPrBase.PStyle pStyle = Context.getWmlObjectFactory().createPPrBasePStyle();
            pStyle.setVal("8");
            pPr.setPStyle(pStyle);
            p.setPPr(pPr);
        }else{
            p = (P) XmlUtils.unmarshalString(getPageNumFooterXml(fromPageNum));
        }
        ftr.getContent().add(p);
        return ftr;
    }

    private static void createFooterReference(SectPr sectPr, Relationship relationship){
        //创建页码格式
        CTPageNumber pageNumber = Context.getWmlObjectFactory().createCTPageNumber();
        pageNumber.setFmt(NumberFormat.DECIMAL);
        sectPr.setPgNumType(pageNumber);
        //添加引用
        addReferences(sectPr, relationship);
    }

    public static void addReferences(SectPr sectPr,Relationship relationship){
        FooterReference footerReference = Context.getWmlObjectFactory().createFooterReference();
        footerReference.setId(relationship.getId());
        footerReference.setType(HdrFtrRef.DEFAULT);
        sectPr.getEGHdrFtrReferences().add(footerReference);
    }



}

TocGenerator

java

import org.docx4j.XmlUtils;
import org.docx4j.jaxb.Context;
import org.docx4j.model.listnumbering.Emulator;
import org.docx4j.model.structure.PageDimensions;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.toc.*;
import org.docx4j.toc.switches.SwitchProcessor;
import org.docx4j.wml.*;

import javax.xml.bind.JAXBElement;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName TocGenerator
 * @Author wuzm
 * @Date 2021/1/11 15:45
 * @Version 1.0
 */
public class TocGenerator extends org.docx4j.toc.TocGenerator {
    private WordprocessingMLPackage wordMLPackage;
    private SectPr sectPr;
    private TocStyles tocStyles;

    public TocGenerator(WordprocessingMLPackage wordMLPackage) throws TocException {
        super(wordMLPackage);
        this.wordMLPackage = wordMLPackage;
        //获取父类的属性
        try {
            Method getTocStylesMethod = this.getClass().getSuperclass().getDeclaredMethod("getTocStyles", MainDocumentPart.class);
            getTocStylesMethod.setAccessible(true);
            tocStyles = (TocStyles) getTocStylesMethod.invoke(this, wordMLPackage.getMainDocumentPart());
        }catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected SdtBlock generateToc(SdtBlock sdt, String instruction, STTabTlc leader, boolean skipPageNumbering) throws TocException {
        MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
        SdtContentBlock sdtContent = (SdtContentBlock)sdt.getSdtContent();

        if(sdtContent == null){
            sdtContent = Context.getWmlObjectFactory().createSdtContentBlock();
            sdt.setSdtContent(sdtContent);
        }

        try {
            Method getTocStylesMethod = tocStyles.getClass().getDeclaredMethod("getStyleIdForName",String.class);
            getTocStylesMethod.setAccessible(true);
            String TOC_HEADING_STYLE = (String) getTocStylesMethod.invoke(tocStyles, TocStyles.TOC_HEADING);
            if (TOC_HEADING_STYLE == null) {

                String HEADING1_STYLE = (String) getTocStylesMethod.invoke(tocStyles, TocStyles.HEADING_1);

                if (TOC_HEADING_STYLE == null) {
                    // We need to create it.
                    if (HEADING1_STYLE == null) {
                        Style style = (Style) XmlUtils.unmarshalString(XML_TOCHeading_BasedOn_Nothing);
                        style.getBasedOn().setVal(HEADING1_STYLE);
                        documentPart.getStyleDefinitionsPart().getContents().getStyle().add(style);

                    } else {
                        // There is a heading 1 style, so use a simple style based on that
                        Style style = (Style) XmlUtils.unmarshalString(XML_TOCHeading_BasedOn_Heading1);
                        style.getBasedOn().setVal(HEADING1_STYLE);
                        documentPart.getStyleDefinitionsPart().getContents().getStyle().add(style);
                    }

                    // either way,
                    TOC_HEADING_STYLE = "TOCHeading";
                }
            }
            sdtContent.getContent().add(generateTocHeading(TOC_HEADING_STYLE,"目录"));
        } catch (Exception e) {
            throw new TocException(e.getMessage(),e);
        }
        populateToc(sdtContent, instruction, leader);
        return sdt;
    }

    //添加目录标题
    private static P generateTocHeading(String headingStyleId, String tocHeading) {
        ObjectFactory wmlObjectFactory = Context.getWmlObjectFactory();
        // Create object for p
        P p = wmlObjectFactory.createP();
        // Create object for pPr
        PPr ppr = wmlObjectFactory.createPPr();
        p.setPPr(ppr);

        Jc jc = wmlObjectFactory.createJc();
        jc.setVal(JcEnumeration.CENTER);
        ppr.setJc(jc);

        // Create object for pStyle
        PPrBase.PStyle pprbasepstyle = wmlObjectFactory.createPPrBasePStyle();
        ppr.setPStyle(pprbasepstyle);
        pprbasepstyle.setVal(headingStyleId);
        // Create object for r
        R r = wmlObjectFactory.createR();
        p.getContent().add(r);
        // Create object for t (wrapped in JAXBElement)
        Text text = wmlObjectFactory.createText();
        JAXBElement<Text> textWrapped = wmlObjectFactory.createRT(text);
        r.getContent().add(textWrapped);
        text.setValue(tocHeading);
        return p;
    }

    //填充目录内容
    private  void populateToc(
            SdtContentBlock sdtContent,
            String instruction, STTabTlc leader) throws TocException {

        MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
        Document wmlDocumentEl = documentPart.getJaxbElement();
        Body body =  wmlDocumentEl.getBody();

        // Generate new TOC
        Toc toc = new Toc(instruction); // will throw an exception if it can't be parsed

        // Process Toc switches

        // .. we need page dimensions for right aligned tab (for page numbers)
        // It is reasonable to require there to be a sectPr containing the necessary info
        if (sectPr == null) {
            // it doesn't fall back to the body level one
            sectPr = this.wordMLPackage.getMainDocumentPart().getJaxbElement().getBody().getSectPr();
            if (sectPr == null) {
                throw new TocException("No sectPr following ToC");
            }
        }
        PageDimensions pageDimensions = new PageDimensions(sectPr);
        try {
            pageDimensions.getWritableWidthTwips();
        } catch (RuntimeException e) {
            throw new TocException("margins or page width not defined in \n" + XmlUtils.marshaltoString(sectPr));
        }

        List<TocEntry> tocEntries = new ArrayList<TocEntry>();

        @SuppressWarnings("unchecked")
        List<P> pList = (List<P>)(List<?>) TocHelper.getAllElementsFromObject(body, P.class);

        // Work out paragraph numbering
        Map<P, Emulator.ResultTriple> pNumbersMap = numberParagraphs( pList);

        SwitchProcessor sp = new SwitchProcessor(pageDimensions, leader);

        tocEntries.addAll(
                sp.processSwitches(wordMLPackage, pList, toc.getSwitches(), pNumbersMap));


        if (tocEntries.size()==0) {
            P p = new P();
            p.getContent().addAll(toc.getTocInstruction());
            sdtContent.getContent().add(p);
            sdtContent.getContent().add(toc.getLastParagraph());
        } else {
            // Prep: merge instruction into first tocEntry (avoiding an unwanted additional paragraph)
            P firstEntry = tocEntries.get(0).getEntryParagraph(tocStyles);
            firstEntry.getContent().addAll(0, toc.getTocInstruction());

            // Add Toc Entries paragraphs
            for(TocEntry entry: tocEntries){
                sdtContent.getContent().add(entry.getEntryParagraph(tocStyles));
            }

            // Add last toc paragraph
            sdtContent.getContent().add(toc.getLastParagraph());

//            // Add page numbers
//            if(!skipPageNumbering && sp.pageNumbers() ) {
//                for(TocEntry entry: tocEntries){
//                    entry.getEntryPageNumberText().setValue(Integer.toString((1)));
//                }
//            }
        }

    }

    private  Map<P, Emulator.ResultTriple> numberParagraphs(List<P> pList) {

        org.docx4j.openpackaging.parts.WordprocessingML.NumberingDefinitionsPart numberingPart =
                wordMLPackage.getMainDocumentPart().getNumberingDefinitionsPart();

        Map<P, Emulator.ResultTriple> pNumbersMap = new HashMap<>();
        if (numberingPart==null) {
            return pNumbersMap;
        }

        numberingPart.getEmulator(true); // reset counters

        for (P p : pList) {

            if (p.getPPr()!=null) {

                Emulator.ResultTriple triple = Emulator.getNumber(wordMLPackage, p.getPPr());
                pNumbersMap.put(p, triple);
            }
        }
        return pNumbersMap;
    }

    // Note these are only used if the style is not defined in the docx,
    // nor in the default styles read by TocStyles.
    private static String XML_TOCHeading_BasedOn_Nothing = "<w:style w:styleId=\"TOCHeading\" w:type=\"paragraph\" xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\">"
            + "<w:name w:val=\"TOC Heading\"/>"
            // + "<w:basedOn w:val=\"Heading1\"/>"  // would be ok if provided already present, since
            + "<w:next w:val=\"Normal\"/>"
            + "<w:uiPriority w:val=\"39\"/>"
            + "<w:semiHidden/>"
            + "<w:unhideWhenUsed/>"
            + "<w:qFormat/>"
            + "<w:pPr>"
            + "<w:keepNext/>"
            + "<w:keepLines/>"
            + "<w:spacing w:after=\"0\" w:before=\"480\"/>"
            + "<w:outlineLvl w:val=\"9\"/>"
            +"</w:pPr>"
            + "<w:rPr>"
            + "<w:rFonts w:asciiTheme=\"majorHAnsi\" w:cstheme=\"majorBidi\" w:eastAsiaTheme=\"majorEastAsia\" w:hAnsiTheme=\"majorHAnsi\"/>"
            + "<w:b/>"
            + "<w:bCs/>"
            + "<w:color w:themeColor=\"accent1\" w:themeShade=\"BF\" w:val=\"365F91\"/>"
            + "<w:sz w:val=\"28\"/>"
            + "<w:szCs w:val=\"28\"/>"
            +"</w:rPr>"
            +"</w:style>";

    private static String XML_TOCHeading_BasedOn_Heading1 = "<w:style w:styleId=\"TOCHeading\" w:type=\"paragraph\" xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\">"
            + "<w:name w:val=\"TOC Heading\"/>"
            + "<w:basedOn w:val=\"Heading1\"/>" // we'll overwrite with the style id
            + "<w:next w:val=\"Normal\"/>"
            + "<w:uiPriority w:val=\"39\"/>"
            + "<w:semiHidden/>"
            + "<w:unhideWhenUsed/>"
            + "<w:qFormat/>"
            + "<w:pPr>"
            + "<w:outlineLvl w:val=\"9\"/>"
            +"</w:pPr>"
            +"</w:style>";
}

父接口

定义相关属性及方法

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.file.model.FileEntity;

import java.io.File;
import java.util.List;

/**
 * @ClassName IFileConverter
 * @Author wuzm
 * @Date 2020/9/21 15:07
 * @Version 1.0
 */
public interface IFileConverter {

    /**
     * 原文件类型
     * @author wuzm
     * @date 2020/9/21
     */
    String sourceFileType();

    /**
     * 目标文件类型
     * @author wuzm
     * @date 2020/9/21
     */
    String targetFileType();

    /**
     * 是否允许转换
     * @author wuzm
     * @date 2020/9/23
     */
    boolean enableConvert();

    /**
     * 是否允许合并
     * @author wuzm
     * @date 2020/9/23
     */
    boolean enableMerge();

    /**
     * 具体文件转换方法
     * targetFileEntity中只包含部分转换后文件信息,
     * 如name, spaceid, uploadid, mimetype, suffix, expiretime, accesstype, owner, createtime, updatetime
     * @author wuzm
     * @date 2020/9/21
     */
    Result<List<File>> convert(FileEntity sourceFileEntity , FileEntity targetFileEntity) throws Exception;

    /**
     * 具体文件合并方法
     * @param sourceFileEntity 需要合并的文件实体
     * @param targetFileEntity 转换后文件部分信息
     * @param targetFile 合并后的文件
     * @return com.commnetsoft.commons.Result<java.lang.Void>
     * @author wuzm
     * @date 2020/9/23
     */
    Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception;
}

公共抽象类

提供获取文件输入流

java

import com.commnetsoft.file.model.FileEntity;
import com.commnetsoft.file.utils.FileHandlerFactory;
import com.commnetsoft.file.utils.IFileReadHandler;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;

/**
 * @ClassName AbstractFileConverter
 * @Author wuzm
 * @Date 2020/9/21 15:36
 * @Version 1.0
 */
public abstract class AbstractFileConverter implements IFileConverter {
    public final static String FILE_TYPE_TXT = "txt";
    public final static String FILE_TYPE_DOC = "doc";
    public final static String FILE_TYPE_DOCX = "docx";
    public final static String FILE_TYPE_PDF = "pdf";
    public final static String FILE_TYPE_ZIP = "zip";
    public final static String FILE_TYPE_XLS = "xls";
    public final static String FILE_TYPE_XLSX = "xlsx";


    /**
     * 获取文件输流
     * @param sourceFileEntity 原文件对象
     * @return java.io.InputStream
     * @author wuzm
     * @date 2020/9/22
     */
    protected InputStream getFileInputStream(FileEntity sourceFileEntity) throws IOException {
        URI newUri = URI.create(sourceFileEntity.getUrl());
        IFileReadHandler fileReadHandler = FileHandlerFactory.getFileReadHandler(newUri.getScheme());
        return fileReadHandler.readFile(sourceFileEntity.getUrl(), sourceFileEntity);
    }
}

各个类型转换类

Word类型

doc类型

抽象类

java

import com.commnetsoft.file.converter.AbstractFileConverter;

/**
 * @ClassName AbstractWordConverter
 * @Author wuzm
 * @Date 2020/9/21 15:45
 * @Version 1.0
 */
public abstract class AbstractDocConverter extends AbstractFileConverter {
    @Override
    public String sourceFileType() {
        return "doc";
    }
}

转换各个类型实现类

转换为docx

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.WordUtil;
import com.commnetsoft.file.converter.word.AbstractDocConverter;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName DocToDocxConverter
 * @Author wujs
 * @Date 2020/9/24 16:38 
 * @Version 1.0
 */
@Component
public class DocToDocxConverter extends AbstractDocConverter {


    @Override
    public String targetFileType() {
        return "docx";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = WordUtil.wordConvert(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "文件转换失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为jpeg

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.converter.word.AbstractDocConverter;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName DocToJpegConverter
 * @Author wujs
 * @Date 2020/9/24 16:44 
 * @Version 1.0
 */
@Component
public class DocToJpegConverter extends AbstractDocConverter {

    @Override
    public String targetFileType() {
        return "jpeg";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.wordToPictureByType(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "doc文件转jpeg失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为jpg

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.converter.word.AbstractDocConverter;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName DocToJpgConverter
 * @Author wujs
 * @Date 2020/9/24 16:44 
 * @Version 1.0
 */
@Component
public class DocToJpgConverter extends AbstractDocConverter {
    @Override
    public String targetFileType() {
        return "jpg";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.wordToPictureByType(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "doc文件转jpg失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为pdf

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.WordUtil;
import com.commnetsoft.file.converter.word.AbstractDocConverter;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName DocToPdf
 * @Author wujs
 * @Date 2020/9/24 16:42 
 * @Version 1.0
 */
@Component
public class DocToPdfConverter extends AbstractDocConverter {
    @Override
    public String targetFileType() {
        return "pdf";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = WordUtil.wordConvert(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "doc转pdf文件失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为png

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.converter.word.AbstractDocConverter;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName DocToPngConverter
 * @Author wujs
 * @Date 2020/9/24 16:45 
 * @Version 1.0
 */
@Component
public class DocToPngConverter extends AbstractDocConverter {
    @Override
    public String targetFileType() {
        return "png";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.wordToPictureByType(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "doc文件转png失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为txt类型

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.WordUtil;
import com.commnetsoft.file.converter.word.AbstractDocConverter;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName DocToTxtConverter
 * @Author wujs
 * @Date 2020/9/25 11:20 
 * @Version 1.0
 */
@Component
public class DocToTxtConverter extends AbstractDocConverter {

    @Override
    public String targetFileType() {
        return "txt";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = WordUtil.docToTxt(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "文件转换失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

docx类型

抽象类

java

import com.commnetsoft.file.converter.AbstractFileConverter;

/**
 * @ClassName AbstractDocxConverter
 * @Author wujs
 * @Date 2020/9/24 17:16 
 * @Version 1.0
 */
public abstract class AbstractDocxConverter extends AbstractFileConverter {
    @Override
    public String sourceFileType() {
        return "docx";
    }
}

docx转换类

转换为doc

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.WordUtil;
import com.commnetsoft.file.converter.word.AbstractDocxConverter;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName DocxToDocConverter
 * @Author wujs
 * @Date 2020/9/24 16:24 
 * @Version 1.0
 */
@Component
public class DocxToDocConverter extends AbstractDocxConverter {
    @Override
    public String targetFileType() {
        return "doc";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = WordUtil.wordConvert(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "docx转doc文件失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为jpeg

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.converter.word.AbstractDocxConverter;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName DocxToJpegConverter
 * @Author wujs
 * @Date 2020/9/25 9:02 
 * @Version 1.0
 */
@Component
public class DocxToJpegConverter extends AbstractDocxConverter {
    @Override
    public String targetFileType() {
        return "jpeg";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.wordToPictureByType(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "docx文件转jpeg失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为jpg类型

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.converter.word.AbstractDocxConverter;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName DocxToJpgConverter
 * @Author wujs
 * @Date 2020/9/25 9:02 
 * @Version 1.0
 */
@Component
public class DocxToJpgConverter extends AbstractDocxConverter {
    @Override
    public String targetFileType() {
        return "jpg";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.wordToPictureByType(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "docx文件转jpg失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为pdf

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.WordUtil;
import com.commnetsoft.file.converter.word.AbstractDocxConverter;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName DocxToPdfConverter
 * @Author wujs
 * @Date 2020/9/24 9:20 
 * @Version 1.0
 */
@Component
public class DocxToPdfConverter extends AbstractDocxConverter {
    @Override
    public String targetFileType() {
        return "pdf";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = WordUtil.wordConvert(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "docx转pdf文件失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为png

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.converter.word.AbstractDocxConverter;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName DocxToJpgConverter
 * @Author wujs
 * @Date 2020/9/25 9:03 
 * @Version 1.0
 */
@Component
public class DocxToPngConverter extends AbstractDocxConverter {
    @Override
    public String targetFileType() {
        return "png";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.wordToPictureByType(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "docx文件转png失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为txt

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.WordUtil;
import com.commnetsoft.file.converter.word.AbstractDocxConverter;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName DocxToTxtConverter
 * @Author wujs
 * @Date 2020/9/25 13:49 
 * @Version 1.0
 */
@Component
public class DocxToTxtConverter extends AbstractDocxConverter {

    @Override
    public String targetFileType() {
        return "txt";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = WordUtil.docxToTxt(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "文件转换失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

Txt类型

抽象类

java

import com.commnetsoft.file.converter.AbstractFileConverter;

/**
 * @ClassName AbstractTxtConverter
 * @Author wujs
 * @Date 2020/9/25 9:53 
 * @Version 1.0
 */
public abstract class AbstractTxtConverter extends AbstractFileConverter {
    @Override
    public String sourceFileType() {
        return "txt";
    }
}

转doc

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.WordUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName TxtToDocConverter
 * @Author wujs
 * @Date 2020/9/25 9:55 
 * @Version 1.0
 */
@Component
public class TxtToDocConverter extends AbstractTxtConverter {
    @Override
    public String targetFileType() {
        return "doc";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = WordUtil.txtToWordByType(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "txt转doc失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转docx

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.WordUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName TxtToDocxConverter
 * @Author wujs
 * @Date 2020/9/25 9:55 
 * @Version 1.0
 */
@Component
public class TxtToDocxConverter extends AbstractTxtConverter{
    @Override
    public String targetFileType() {
        return "docx";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = WordUtil.txtToWordByType(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "txt转doc失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转pdf

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.PdfUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName TxtToPdfConverter
 * @Author wujs
 * @Date 2020/9/28 9:38 
 * @Version 1.0
 */
@Component
public class TxtToPdfConverter extends AbstractTxtConverter {
    @Override
    public String targetFileType() {
        return "pdf";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PdfUtil.txtToPdf(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "txt转pdf失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

Picture类型

jpeg类型

父抽象类

java

import com.commnetsoft.file.converter.AbstractFileConverter;

/**
 * @ClassName AbstractJpegConverter
 * @Author wujs
 * @Date 2020/9/25 9:26 
 * @Version 1.0
 */
public abstract class AbstractJpegConverter extends AbstractFileConverter {
    @Override
    public String sourceFileType() {
        return "jpeg";
    }
}

转jpg

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.picture.AbstractJpegConverter;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName JpegToJpgConverter
 * @Author wujs
 * @Date 2020/9/25 9:38 
 * @Version 1.0
 */
@Component
public class JpegToJpgConverter extends AbstractJpegConverter {
    @Override
    public String targetFileType() {
        return "jpg";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.pictureConvertByType(inputStream, targetFileType(),sourceFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "jpeg转jpg失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转pdf

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.picture.AbstractJpegConverter;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName JpegToPdfConverter
 * @Author wujs
 * @Date 2020/9/25 9:23 
 * @Version 1.0
 */
@Component
public class JpegToPdfConverter extends AbstractJpegConverter {
    @Override
    public String targetFileType() {
        return "pdf";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.pictureToPdf(inputStream,targetFileType());
        if (null == file){
            return Result.create(CommonError.internal_error,"jpeg转pdf失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转png

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.picture.AbstractJpegConverter;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName JpegToPngConverter
 * @Author wujs
 * @Date 2020/9/25 9:43 
 * @Version 1.0
 */
@Component
public class JpegToPngConverter extends AbstractJpegConverter {
    @Override
    public String targetFileType() {
        return "png";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.pictureConvertByType(inputStream, targetFileType(),sourceFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "jpeg转jpg失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

jpg类型

父抽象类

java

import com.commnetsoft.file.converter.AbstractFileConverter;

/**
 * @ClassName AbstractJpgConverter
 * @Author wujs
 * @Date 2020/9/25 9:25 
 * @Version 1.0
 */
public abstract class AbstractJpgConverter extends AbstractFileConverter {
    @Override
    public String sourceFileType() {
        return "jpg";
    }
}

转jpeg

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.picture.AbstractJpgConverter;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName JpgToJpegConverter
 * @Author wujs
 * @Date 2020/9/25 9:45 
 * @Version 1.0
 */
@Component
public class JpgToJpegConverter extends AbstractJpgConverter {
    @Override
    public String targetFileType() {
        return "jpeg";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.pictureConvertByType(inputStream, targetFileType(),sourceFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "jpg转jpeg失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转pdf

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.picture.AbstractJpgConverter;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName JpgToPdfConverter
 * @Author wujs
 * @Date 2020/9/25 9:22 
 * @Version 1.0
 */
@Component
public class JpgToPdfConverter extends AbstractJpgConverter {
    @Override
    public String targetFileType() {
        return "pdf";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.pictureToPdf(inputStream,targetFileType());
        if (null == file){
            return Result.create(CommonError.internal_error,"jpeg转pdf失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转png

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.picture.AbstractJpgConverter;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName JpgToPngConverter
 * @Author wujs
 * @Date 2020/9/25 9:45 
 * @Version 1.0
 */
@Component
public class JpgToPngConverter extends AbstractJpgConverter {
    @Override
    public String targetFileType() {
        return "png";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.pictureConvertByType(inputStream, targetFileType(),sourceFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "jpeg转jpg失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

png类型

父抽象类

java

import com.commnetsoft.file.converter.AbstractFileConverter;

/**
 * @ClassName AbstractPngConverter
 * @Author wujs
 * @Date 2020/9/25 9:27 
 * @Version 1.0
 */
public abstract class AbstractPngConverter extends AbstractFileConverter {
    @Override
    public String sourceFileType() {
        return "png";
    }
}

转jpeg

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.picture.AbstractPngConverter;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName PngToJpegConverter
 * @Author wujs
 * @Date 2020/9/25 9:46 
 * @Version 1.0
 */
@Component
public class PngToJpegConverter extends AbstractPngConverter {
    @Override
    public String targetFileType() {
        return "jpeg";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.pictureConvertByType(inputStream, targetFileType(),sourceFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "jpeg转jpg失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转jpg

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.picture.AbstractPngConverter;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName PngToJpgConverter
 * @Author wujs
 * @Date 2020/9/25 9:46 
 * @Version 1.0
 */
@Component
public class PngToJpgConverter extends AbstractPngConverter {
    @Override
    public String targetFileType() {
        return "jpg";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.pictureConvertByType(inputStream, targetFileType(),sourceFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "jpeg转jpg失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转pdf

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.picture.AbstractPngConverter;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName PngToPdfConverter
 * @Author wujs
 * @Date 2020/9/25 9:24 
 * @Version 1.0
 */
@Component
public class PngToPdfConverter extends AbstractPngConverter {
    @Override
    public String targetFileType() {
        return "pdf";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.pictureToPdf(inputStream,targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "jpeg转pdf失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

Pdf类型

父抽象类

java

import com.commnetsoft.file.converter.AbstractFileConverter;

/**
 * @ClassName AbstractPdfConverter
 * @Author wuzm
 * @Date 2020/9/21 15:35
 * @Version 1.0
 */
public abstract class AbstractPdfConverter extends AbstractFileConverter {

    @Override
    public String sourceFileType() {
        return "pdf";
    }
}

转换为doc

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.PdfUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName PdfToDocConverter
 * @Author wujs
 * @Date 2020/9/24 15:50 
 * @Version 1.0
 */
@Component
public class PdfToDocConverter extends AbstractPdfConverter{
    @Override
    public String targetFileType() {
        return "doc";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PdfUtil.pdfToWord(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "文件转换失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为docx

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.PdfUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName PdfToDocxConverter
 * @Author wujs
 * @Date 2020/9/24 16:00 
 * @Version 1.0
 */
@Component
public class PdfToDocxConverter extends AbstractPdfConverter {
    @Override
    public String targetFileType() {
        return "docx";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PdfUtil.pdfToWord(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "文件转换失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为jpeg

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName PdfToJpegConverter
 * @Author wujs
 * @Date 2020/9/24 16:05 
 * @Version 1.0
 */
@Component
public class PdfToJpegConverter extends AbstractPdfConverter {
    @Override
    public String targetFileType() {
        return "jpeg";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.pdfToPictureByType(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "文件转换失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为jpg

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName PdfToJpgConverter
 * @Author wujs
 * @Date 2020/9/24 16:04 
 * @Version 1.0
 */
@Component
public class PdfToJpgConverter extends AbstractPdfConverter {
    @Override
    public String targetFileType() {
        return "jpg";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.pdfToPictureByType(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "文件转换失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为png

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.util.PictureUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName PdfToPngConverter
 * @Author wujs
 * @Date 2020/9/24 16:06 
 * @Version 1.0
 */
@Component
public class PdfToPngConverter extends AbstractPdfConverter {
    @Override
    public String targetFileType() {
        return "png";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = PictureUtil.pdfToPictureByType(inputStream, targetFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "文件转换失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为txt

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.commons.utils.UUIDUtils;
import com.commnetsoft.file.model.FileEntity;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName PdfToTxtConverter
 * @Author wuzm
 * @Date 2020/9/21 15:35
 * @Version 1.0
 */
@Component
public class PdfToTxtConverter extends AbstractPdfConverter {

    @Override
    public String targetFileType() {
        return "txt";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = null;
        Writer writer = null;
        PDDocument pdDocument = null;
        try {
            inputStream = getFileInputStream(sourceFileEntity);
            File tempDirPath = new File(System.getProperty("java.io.tmpdir"));
            File txtFile = new File(tempDirPath, UUIDUtils.generate() + "." + targetFileType());
            writer = new OutputStreamWriter(new FileOutputStream(txtFile), "UTF-8");
            pdDocument = PDDocument.load(inputStream);
            PDFTextStripper pdfTextStripper = new PDFTextStripper();
            pdfTextStripper.writeText(pdDocument, writer);
            return Result.create(Arrays.asList(txtFile));
        } finally {
            if(null != inputStream) inputStream.close();
            if (null != writer) writer.close();
            if (null != pdDocument) pdDocument.close();
        }
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }

}

Html类型

父抽象类

java

import com.commnetsoft.file.converter.AbstractFileConverter;
import org.springframework.stereotype.Component;

/**
 * @ClassName AbstractHtmlConverter
 * @Author wujs
 * @Date 2021/1/28 14:54 
 * @Version 1.0
 */
@Component
public abstract class AbstractHtmlConverter extends AbstractFileConverter {
    @Override
    public String sourceFileType() {
        return "html";
    }
}

转docx

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.file.converter.util.PdfUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName HtmlTodocxConverter
 * @Author wujs
 * @Date 2021/1/28 15:05 
 * @Version 1.0
 */
@Component
public class HtmlTodocxConverter extends AbstractHtmlConverter {

    @Autowired
    private PdfUtil pdfUtil;

    @Override
    public String targetFileType() {
        return "docx";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        return Result.create(Arrays.asList(pdfUtil.convert(inputStream,targetFileType())));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转pdf

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.file.converter.util.PdfUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName HtmlToPdfConverter
 * @Author wujs
 * @Date 2021/1/28 14:55
 * @Version 1.0
 */
@Component
public class HtmlToPdfConverter extends AbstractHtmlConverter {

    @Autowired
    private PdfUtil pdfUtil;

    @Override
    public String targetFileType() {
        return "pdf";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {

        InputStream inputStream = getFileInputStream(sourceFileEntity);
        return Result.create(Arrays.asList(pdfUtil.convert(inputStream,targetFileType())));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

Excel类型

xls类型

父抽象类

java

import com.commnetsoft.file.converter.AbstractFileConverter;

/**
 * @ClassName AbstractExcelConverter
 * @Author wujs
 * @Date 2020/9/24 9:21 
 * @Version 1.0
 */
public abstract class AbstractXlsConverter extends AbstractFileConverter {

    @Override
    public String sourceFileType() {
        return "xls";
    }
}

转换为doc

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.excel.AbstractXlsConverter;
import com.commnetsoft.file.converter.util.ExcelUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName XlsToDocConverter
 * @Author wujs
 * @Date 2020/9/27 9:59 
 * @Version 1.0
 */
@Component
public class XlsToDocConverter extends AbstractXlsConverter {
    @Override
    public String targetFileType() {
        return "doc";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {

        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File xlsToDocx = ExcelUtil.excelToWord(inputStream, targetFileType(),sourceFileType());
        if (null == xlsToDocx) {
            return Result.create(CommonError.internal_error, "xls转doc失败!");
        }
        return Result.create(Arrays.asList(xlsToDocx));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为docx

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.excel.AbstractXlsConverter;
import com.commnetsoft.file.converter.util.ExcelUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName XlsToDocConverter
 * @Author wujs
 * @Date 2020/9/27 10:00 
 * @Version 1.0
 */
@Component
public class XlsToDocxConverter extends AbstractXlsConverter {
    @Override
    public String targetFileType() {
        return "docx";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File xlsToDocx = ExcelUtil.excelToWord(inputStream, targetFileType(),sourceFileType());
        if (null == xlsToDocx) {
            return Result.create(CommonError.internal_error, "xls转docx失败!");
        }
        return Result.create(Arrays.asList(xlsToDocx));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为pdf

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.excel.AbstractXlsConverter;
import com.commnetsoft.file.converter.util.ExcelUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;



/**
 * @ClassName XlsToPdfConverter
 * @Author wujs
 * @Date 2020/9/27 10:51 
 * @Version 1.0
 */
@Component
public class XlsToPdfConverter extends AbstractXlsConverter {
    @Override
    public String targetFileType() {
        return "pdf";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
      InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = ExcelUtil.excelTopdf(inputStream, targetFileType(),sourceFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "xls转pdf失败");
        }
        return Result.create(Arrays.asList(file));

    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为txt

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.excel.AbstractXlsConverter;
import com.commnetsoft.file.converter.util.ExcelUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName ExcelToTxtConverter
 * @Author wujs
 * @Date 2020/9/25 9:25 
 * @Version 1.0
 */
@Component
public class XlsToTxtConverter extends AbstractXlsConverter {

    @Override
    public String targetFileType() {
        return "txt";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = ExcelUtil.excelToTxt(inputStream, targetFileType(),sourceFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "文件转换失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

xlsx类型

父抽象类

java

import com.commnetsoft.file.converter.AbstractFileConverter;

/**
 * @ClassName AbstractXlsxConverter
 * @Author wujs
 * @Date 2020/9/25 9:51 
 * @Version 1.0
 */
public abstract class AbstractXlsxConverter extends AbstractFileConverter {
    @Override
    public String sourceFileType() {
        return "xlsx";
    }
}

转换为doc

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.excel.AbstractXlsxConverter;
import com.commnetsoft.file.converter.util.ExcelUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;

/**
 * @ClassName XlsxToDocConverter
 * @Author wujs
 * @Date 2020/927 10:00 
 * @Version 1.0
 */
@Component
public class XlsxToDocConverter extends AbstractXlsxConverter {
    @Override
    public String targetFileType() {
        return "doc";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File xlsToDocx = ExcelUtil.excelToWord(inputStream, targetFileType(),sourceFileType());
        if (null == xlsToDocx) {
            return Result.create(CommonError.internal_error, "xlsx转doc失败!");
        }
        return Result.create(Collections.singletonList(xlsToDocx));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为docx

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.excel.AbstractXlsxConverter;
import com.commnetsoft.file.converter.util.ExcelUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName XlsxToDocxConverter
 * @Author wujs
 * @Date 2020/927 10:00 
 * @Version 1.0
 */
@Component
public class XlsxToDocxConverter extends AbstractXlsxConverter {
    @Override
    public String targetFileType() {
        return "docx";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File xlsToDocx = ExcelUtil.excelToWord(inputStream, targetFileType(), sourceFileType());
        if (null == xlsToDocx) {
            return Result.create(CommonError.internal_error, "xlsx转doc失败!");
        }
        return Result.create(Arrays.asList(xlsToDocx));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为pdf

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.excel.AbstractXlsxConverter;
import com.commnetsoft.file.converter.util.ExcelUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName XlsxToPdfConverter
 * @Author wujs
 * @Date 2020/9/27 17:27 
 * @Version 1.0
 */
@Component
public class XlsxToPdfConverter extends AbstractXlsxConverter {
    @Override
    public String targetFileType() {
        return "pdf";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = ExcelUtil.excelTopdf(inputStream, targetFileType(), sourceFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "xlsx转pdf失败");
        }
        return Result.create(Arrays.asList(file));

    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}

转换为txt

java

import com.commnetsoft.commons.Result;
import com.commnetsoft.core.CommonError;
import com.commnetsoft.file.converter.excel.AbstractXlsxConverter;
import com.commnetsoft.file.converter.util.ExcelUtil;
import com.commnetsoft.file.model.FileEntity;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

/**
 * @ClassName XlsxToTxtConverter
 * @Author wujs
 * @Date 2020/9/25 10:48 
 * @Version 1.0
 */
@Component
public class XlsxToTxtConverter extends AbstractXlsxConverter {

    @Override
    public String targetFileType() {
        return "txt";
    }

    @Override
    public boolean enableConvert() {
        return true;
    }

    @Override
    public boolean enableMerge() {
        return false;
    }

    @Override
    public Result<List<File>> convert(FileEntity sourceFileEntity, FileEntity targetFileEntity) throws Exception {
        InputStream inputStream = getFileInputStream(sourceFileEntity);
        File file = ExcelUtil.excelToTxt(inputStream, targetFileType(),sourceFileType());
        if (null == file) {
            return Result.create(CommonError.internal_error, "文件转换失败");
        }
        return Result.create(Arrays.asList(file));
    }

    @Override
    public Result<Void> merge(FileEntity sourceFileEntity, FileEntity targetFileEntity, File targetFile) throws Exception {
        return null;
    }
}