liurunyu
2024-04-23 1300e2434cb457e7d4d06ea66d90a04492f6c4e9
1、完善系统架构,增加了分布式web文件模块
1 文件已重命名
28个文件已添加
5个文件已修改
2534 ■■■■■ 已修改文件
pms-parent/pms-common/src/main/java/com/dy/common/util/MurmurHash.java 302 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/daoOth/OthFileMapper.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/dyFile/FileConstant.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/dyFile/FileOperate.java 301 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/dyFile/FileRestVo.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/dyFile/FileVo.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/dyFile/NameValue.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/global/WebFileCtrl.java 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/global/WebFileSv.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/pojoOth/OthFile.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-global/src/main/resources/application-global.yml 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-global/src/main/resources/mapper/OthFileMapper.xml 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-base/src/main/java/com/dy/pmsBase/PmsBaseApplication.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-file/pom.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/DyFileApplication.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/download/DownloadFileCtr.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/files/FileCtrl.java 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/util/FileConstant.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/util/FileDocumentUtil.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/util/FileIconUtil.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/util/FilePhoneUtil.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/util/FilePhotoUtil.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/util/FileUtil.java 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/util/FileVideoUtil.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/util/ZipImg.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-file/src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-other/src/main/java/com/dy/pmsOther/dyFm/DyFileSvConf.java 159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-other/src/main/java/com/dy/pmsOther/dyFm/DyFmListener.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-other/src/main/java/com/dy/pmsOther/dyFm/FileName.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-other/src/main/java/com/dy/pmsOther/dyFm/FileNameIdUtil.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-other/src/main/java/com/dy/pmsOther/dyFm/FileRestVo.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-other/src/main/java/com/dy/pmsOther/dyFm/FmCtrl.java 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-web-other/src/main/java/com/dy/pmsOther/dyFm/RestHashDeal.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pom.xml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pms-parent/pms-common/src/main/java/com/dy/common/util/MurmurHash.java
New file
@@ -0,0 +1,302 @@
package com.dy.common.util;
/**
 * MurmurHash算法:高运算性能,低碰撞率,由Austin Appleby创建于2008年,
 * 现已应用到Hadoop、libstdc++、nginx、libmemcached等开源系统。2011年
 * Appleby被Google雇佣,随后Google推出其变种的CityHash算法。
 *
 * 官方网站:https://sites.google.com/site/murmurhash/
 *
 * MurmurHash算法,自称超级快的hash算法,是FNV的4-5倍。官方数据如下:
 *
 * OneAtATime – 354.163715 mb/sec
 * FNV – 443.668038 mb/sec
 * SuperFastHash – 985.335173 mb/sec
 * lookup3 – 988.080652 mb/sec
 * MurmurHash 1.0 – 1363.293480 mb/sec
 * MurmurHash 2.0 – 2056.885653 mb/sec
 *
 * 但也有文章声称,只有当key的长度大于10字节的时候,MurmurHash的运算速
 * 度才快于DJB。从计算速度上来看,MurmurHash只适用于已知长度的、长度比
 * 较长的字符。
 *
 * 哈希值分布非常均匀,即低碰撞率
 * 比Crc16更为均匀
 */
public final class MurmurHash {
    private byte[] toBytesWithoutEncoding(String str) {
        int len = str.length();
        int pos = 0;
        byte[] buf = new byte[len << 1];
        for (int i = 0; i < len; i++) {
            char c = str.charAt(i);
            buf[pos++] = (byte) (c & 0xFF);
            buf[pos++] = (byte) (c >> 8);
        }
        return buf;
    }
    /**
     * 刘润玉增加方法于2016-12-02
     * @param data
     * @param length
     * @return
     */
    public int hash16_plus(final byte[] data, int length) {
        int hash = hash32(data, length, 0x9747b28c);
        hash = hash % 65535 ;
        if(hash < 0){
            hash = - hash ;
        }
        return hash ;
    }
    /**
     * 刘润玉增加方法于2016-12-02
     * @param data
     * @return
     */
    public int hash16_plus(final String data) {
        byte[] bytes = toBytesWithoutEncoding(data);
        int hash = hash32(bytes, bytes.length, 0x9747b28c);
        hash = hash % 65535 ;
        if(hash < 0){
            hash = - hash ;
        }
        return hash ;
    }
    /**
     * Generates 32 bit hash from byte array with default seed value.
     * @param  data byte array to hash
     * @param length length of the array to hash
     * @return 32 bit hash of the given array
     */
    public int hash32(final byte[] data, int length) {
        return hash32(data, length, 0x9747b28c);
    }
    public int hash32(final String data) {
        byte[] bytes = toBytesWithoutEncoding(data);
        return hash32(bytes, bytes.length, 0x9747b28c);
    }
    /**
     * Generates 64 bit hash from byte array with default seed value.
     * @param  data byte array to hash
     * @param length length of the array to hash
     * @return  64 bit hash of the given string
     */
    public long hash64(final byte[] data, int length) {
        return hash64(data, length, 0xe17a1465);
    }
    public long hash64(final String data) {
        byte[] bytes = toBytesWithoutEncoding(data);
        return hash64(bytes, bytes.length);
    }
    /**
     * Generates 32 bit hash from byte array of the given length and seed.
     * @param data byte array to hash
     * @param length length of the array
     * @param seed initial seed value
     * @return 32 bit hash of the given array
     */
    public int hash32(final byte[] data, int length, int seed) {
        // 'm' and 'r' are mixing constants generated offline.
        // They're not really 'magic', they just happen to work well.
        final int m = 0x5bd1e995;
        final int r = 24;
        // Initialize the hash to a random value
        int h = seed ^ length;
        int length4 = length / 4;
        for (int i = 0; i < length4; i++) {
            final int i4 = i * 4;
            int k = (data[i4 + 0] & 0xff) + ((data[i4 + 1] & 0xff) << 8)
                    + ((data[i4 + 2] & 0xff) << 16)
                    + ((data[i4 + 3] & 0xff) << 24);
            k *= m;
            k ^= k >>> r;
            k *= m;
            h *= m;
            h ^= k;
        }
        // Handle the last few bytes of the input array
        switch (length % 4) {
        case 3:
            h ^= (data[(length & ~3) + 2] & 0xff) << 16;
        case 2:
            h ^= (data[(length & ~3) + 1] & 0xff) << 8;
        case 1:
            h ^= (data[length & ~3] & 0xff);
            h *= m;
        }
        h ^= h >>> 13;
        h *= m;
        h ^= h >>> 15;
        return h;
    }
    /**
     * Generates 64 bit hash from byte array of the given length and seed.
     * @param data byte array to hash
     * @param length length of the array to hash
     * @param seed initial seed value
     * @return 64 bit hash of the given array
     */
    public long hash64(final byte[] data, int length, int seed) {
        final long m = 0xc6a4a7935bd1e995L;
        final int r = 47;
        long h = (seed & 0xffffffffl) ^ (length * m);
        int length8 = length / 8;
        for (int i = 0; i < length8; i++) {
            final int i8 = i * 8;
            long k = ((long) data[i8 + 0] & 0xff)
                    + (((long) data[i8 + 1] & 0xff) << 8)
                    + (((long) data[i8 + 2] & 0xff) << 16)
                    + (((long) data[i8 + 3] & 0xff) << 24)
                    + (((long) data[i8 + 4] & 0xff) << 32)
                    + (((long) data[i8 + 5] & 0xff) << 40)
                    + (((long) data[i8 + 6] & 0xff) << 48)
                    + (((long) data[i8 + 7] & 0xff) << 56);
            k *= m;
            k ^= k >>> r;
            k *= m;
            h ^= k;
            h *= m;
        }
        switch (length % 8) {
        case 7:
            h ^= (long) (data[(length & ~7) + 6] & 0xff) << 48;
        case 6:
            h ^= (long) (data[(length & ~7) + 5] & 0xff) << 40;
        case 5:
            h ^= (long) (data[(length & ~7) + 4] & 0xff) << 32;
        case 4:
            h ^= (long) (data[(length & ~7) + 3] & 0xff) << 24;
        case 3:
            h ^= (long) (data[(length & ~7) + 2] & 0xff) << 16;
        case 2:
            h ^= (long) (data[(length & ~7) + 1] & 0xff) << 8;
        case 1:
            h ^= (long) (data[length & ~7] & 0xff);
            h *= m;
        }
        ;
        h ^= h >>> r;
        h *= m;
        h ^= h >>> r;
        return h;
    }
    public static void main(String[] args) {
        test1() ;
        test2() ;
    }
    public static void test1() {
        String regionNum = "110000" ;
        String temp = "" ;
        String rtuAddr = "" ;
        int total0 = 0 ;
        int total1 = 0 ;
        int total2 = 0 ;
        int total3 = 0 ;
        int total4 = 0 ;
        int total5 = 0 ;
        int totalx = 0 ;
        int totaly = 0 ;
        MurmurHash mhash = new MurmurHash() ;
        Long start = System.currentTimeMillis() ;
        for(int i = 0 ; i < 10000; i++){
            temp = "" + i ;
            while(temp.length() < 5){
                temp = "0" + temp ;
            }
            rtuAddr = regionNum + temp ;
            int hash = mhash.hash16_plus(rtuAddr) ;
            if(hash < 0){
                total0++ ;
            }
            if(hash >= 0 && hash < 1000){
                total1++ ;
            }
            if(hash >= 1000 && hash < 2000){
                total2++ ;
            }
            if(hash >= 2000 && hash < 3000){
                total3++ ;
            }
            if(hash >= 3000 && hash < 4000){
                total4++ ;
            }
            if(hash >= 4000 && hash < 5000){
                total5++ ;
            }
            if(hash >= 64535 && hash < 65535){
                totalx++ ;
            }
            if(hash > 65535){
                totaly++ ;
            }
            //System.out.println(rtuAddr + "-" + crc);
        }
        Long end = System.currentTimeMillis() ;
        System.out.println("用时" + ":" + (end - start));
        System.out.println("0以下" + ":" + total0);
        System.out.println("0-1000" + ":" + total1);
        System.out.println("1000-2000" + ":" + total2);
        System.out.println("2000-3000" + ":" + total3);
        System.out.println("3000-4000" + ":" + total4);
        System.out.println("5000-6000" + ":" + total5);
        System.out.println("64535-65535" + ":" + totalx);
        System.out.println("65535以上" + ":" + totaly);
        System.out.println("=================");
    }
    public static void test2() {
        String regionNum = "110000" ;
        String temp = "" ;
        String rtuAddr = "" ;
        int total1 = 0 ;
        int total2 = 0 ;
        int total3 = 0 ;
        int total4 = 0 ;
        int total5 = 0 ;
        MurmurHash mhash = new MurmurHash() ;
        Long start = System.currentTimeMillis() ;
        for(int i = 0 ; i < 10000; i++){
            temp = "" ;
            temp = "" + i ;
            while(temp.length() < 5){
                temp = "0" + temp ;
            }
            rtuAddr = regionNum + temp ;
            int hash = mhash.hash32(rtuAddr) ;
            if(hash > 0 && hash < 10000000){
                total1++ ;
            }
            if(hash >= 10000000 && hash < 20000000){
                total2++ ;
            }
            if(hash >= 20000000 && hash < 30000000){
                total3++ ;
            }
            if(hash >= 30000000 && hash < 40000000){
                total4++ ;
            }
            if(hash >= 40000000 && hash < 50000000){
                total5++ ;
            }
            //System.out.println(rtuAddr + "-" + crc);
        }
        Long end = System.currentTimeMillis() ;
        System.out.println("用时" + ":" + (end - start));
        System.out.println("0-10000000" + ":" + total1);
        System.out.println("10000000-20000000" + ":" + total2);
        System.out.println("20000000-30000000" + ":" + total3);
        System.out.println("30000000-40000000" + ":" + total4);
        System.out.println("50000000-60000000" + ":" + total5);
        System.out.println("=================");
    }
}
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/daoOth/OthFileMapper.java
New file
@@ -0,0 +1,19 @@
package com.dy.pmsGlobal.daoOth;
import com.dy.pmsGlobal.pojoOth.OthFile;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface OthFileMapper {
    OthFile selectByPrimaryKey(Long id);
    int insert(OthFile record);
    int insertSelective(OthFile record);
    int updateByPrimaryKeySelective(OthFile record);
    int updateByPrimaryKey(OthFile record);
    int deleteByPrimaryKey(Long id);
}
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/dyFile/FileConstant.java
New file
@@ -0,0 +1,31 @@
package com.dy.pmsGlobal.dyFile;
public class FileConstant {
    public static final String NotRegionNum = "-1"; //在web文件系统dyFile中,如果行政区划是"-1",则认为不存在行政区划属性
    public static final String fmRequestMapping = "fm" ;//controller 路径
    public static final String fmPostMapping_create = "create" ;//方法路径
    public static final String fmPostMapping_create_paramName = "fileExtName" ;//参数名称
    public static final String fmPostMapping_parsePath = "parsePath" ;//方法路径
    public static final String fmPostMapping_parsePath_paramName = "filePath" ;//参数名称
    public static final String fmPostMapping_parsePathList = "parsePathList" ;//方法路径
    public static final String fmPostMapping_parsePathList_paramName = "filePaths" ;//参数名称
    public static final String fmPostMapping_parseHashcode = "parseHashcode" ;//方法路径
    public static final String fmPostMapping_parseHashcode_paramName = "hashCode" ;//参数名称
    public static final String fileRequestMapping = "file" ;//controller 路径
    public static final String filePostMapping_photo = "savePhoto" ;//方法路径
    public static final String filePostMapping_phone = "savePhone" ;//上传语音接口路径
    public static final String filePostMapping_video = "saveVideo" ;//上传录像接口路径
    public static final String filePostMapping_document = "saveDocument" ;//上传文档接口路径
    public static final String filePostMapping_paramName_file = "file" ;//参数名称
    public static final String filePostMapping_paramName_regionNum = "regionNum" ;//参数名称
    public static final String filePostMapping_paramName_json = "json" ;//参数名称
    public static final String filePostMapping_paramName_absolutePath = "absolutePath" ;//参数名称
    public static final String filePostMapping_paramName_relativePath = "relativePath" ;//参数名称
    public static final String filePostMapping_paramName_fileName = "fileName" ;//参数名称
    public static final String fileRestDownloadUrl = "DownloadFile!download.action?" ;//下载文件action路径
}
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/dyFile/FileOperate.java
New file
@@ -0,0 +1,301 @@
package com.dy.pmsGlobal.dyFile;
import com.dy.common.util.NumUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import java.util.Arrays;
import java.util.List;
@Component
public class FileOperate {
    @Autowired
    private RestTemplate restTemplate ;
    /**
     * 拆分上载文件的名称
     * @param file
     * @return
     */
    public String[] splitFileName(MultipartFile file){
        String[] grp = new String[2] ;
        if(file != null) {
            String fileName = file.getOriginalFilename();
            int lastDotIndex = fileName.lastIndexOf('.');
            if (lastDotIndex >= 0) {
                grp[0] = fileName.substring(0, lastDotIndex);
                grp[1] = fileName.substring(lastDotIndex + 1);
                if(grp[0].trim().equals("")){
                    grp[0] = "匿名" ;
                }
            }
        }
        return grp ;
    }
    /**
     * 得到上载的文件扩展名
     * @param file
     * @return
     */
    public String getFileExtName(MultipartFile file){
        String fileExtName = null ;
        if(file != null) {
            String filename = file.getOriginalFilename();
            int lastDotIndex = filename.lastIndexOf('.');
            if (lastDotIndex >= 0) {
                fileExtName = filename.substring(lastDotIndex + 1);
            }
        }
        return fileExtName ;
    }
    /**
     * 得到上载的文件主名
     * @param fileName
     * @return
     */
    public String getFileMainName(String fileName){
        String fileMainName = null ;
        if(fileName != null) {
            int lastDotIndex = fileName.lastIndexOf('.');
            if (lastDotIndex >= 0) {
                fileMainName =  fileName.substring(0, lastDotIndex);
            }
        }
        if(fileMainName == null){
            fileMainName = "noName" ;
        }
        return fileMainName ;
    }
    /**
     * 通过照片路径,得到对应缩略图的路径
     * @param imgPath
     * @return
     */
    public String getImgFileZipPath(String imgPath){
        String path_ = null ;
        String prePath = null ;
        String tailPath = null ;
        if(imgPath != null && !imgPath.trim().equals("")) {
            int lastDotIndex = imgPath.lastIndexOf('.');
            if (lastDotIndex >= 0) {
                prePath = imgPath.substring(0, lastDotIndex);
                tailPath = imgPath.substring(lastDotIndex);
                path_ = prePath + "_" + tailPath ;
            }
        }
        if(path_ == null){
            path_ = imgPath ;
        }
        return path_ ;
    }
    /**
     * web分布式文件系统保存文件
     * @param file
     * @param fmUrl
     * @param fileCtrlRqMp
     * @param fileMethodMp
     * @param regionNum
     * @param fileExtName
     * @param json
     * @return
     * @throws Exception
     */
    public FileRestVo saveFile(MultipartFile file,
                               String fmUrl,
                               String fileCtrlRqMp,
                               String fileMethodMp,
                               String regionNum,
                               String fileExtName,
                               String json) throws Exception{
        FileRestVo rvo  = null ;
        if(file != null && file.getBytes() != null && file.getBytes().length > 0){
            rvo = this.restCreateFileName(fmUrl, fileExtName) ;
            if(rvo != null){
                String relativeFilePath = this.restSaveFile(fileCtrlRqMp, fileMethodMp, file, regionNum, json, rvo);
                //log.info("存储文件生成文件路径:" + relativeFilePath);
                if(relativeFilePath != null){
                    rvo.createFilePath(relativeFilePath, rvo.fileNameHash);
                }
            }
        }
        return rvo ;
    }
    /**
     * 生成文件名称
     * @return
     */
    private FileRestVo restCreateFileName(String fmUrl, String fileExtName) throws Exception{
        // 准备请求数据
        MultiValueMap<String, Object> multipartRequestData = new LinkedMultiValueMap<>();
        multipartRequestData.add(FileConstant.fmPostMapping_create_paramName, fileExtName);
        // 设置请求头部,这里假设服务器接收multipart/form-data类型的数据
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        // 封装请求体
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(multipartRequestData, headers);
        String webUrl = fmUrl + "/" + FileConstant.fmRequestMapping + "/" + FileConstant.fmPostMapping_create;
        // 发送POST请求
        FileRestVo rvo = restTemplate.postForObject(webUrl, requestEntity, FileRestVo.class);
        return rvo ;
    }
    /**
     * 把文件存储到文件系统中
     * @param file
     * @param regionNum
     * @param json json数据
     * @param rvo
     * @return
     * @throws Exception
     */
    private String restSaveFile(String fileCtrlRqMp,
                                String fileMethodMp,
                                MultipartFile file,
                                String regionNum,
                                String json,
                                FileRestVo rvo) throws Exception{
        // 准备请求数据
        MultiValueMap<String, Object> multipartRequestData = new LinkedMultiValueMap<>();
        multipartRequestData.add(FileConstant.filePostMapping_paramName_file, file.getResource());
        multipartRequestData.add(FileConstant.filePostMapping_paramName_regionNum, regionNum);
        multipartRequestData.add(FileConstant.filePostMapping_paramName_json, (json==null?"":json));
        multipartRequestData.add(FileConstant.filePostMapping_paramName_absolutePath, rvo.fileSysAbsolutePath);
        multipartRequestData.add(FileConstant.filePostMapping_paramName_relativePath, rvo.fileSysRelativePath);
        multipartRequestData.add(FileConstant.filePostMapping_paramName_fileName, rvo.fileName);
        // 设置请求头部,这里假设服务器接收multipart/form-data类型的数据
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        //headers.setContentLength(file.getSize());
        // 封装请求体
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(multipartRequestData, headers);
        String fileRestUrl = rvo.fileSysRestUrl ;
        if(!fileRestUrl.endsWith("/") && !fileRestUrl.endsWith("\\\\")){
            fileRestUrl += "/" ;
        }
        fileRestUrl += (fileCtrlRqMp + "/" + fileMethodMp) ;
        // 发送POST请求
        return restTemplate.postForObject(fileRestUrl, requestEntity, Object.class).toString();
    }
    /**
     * 解析文件名称
     * @param fmUrl
     * @param filePath
     * @return
     */
    public FileRestVo parse(String fmUrl, String filePath){
        // 准备请求数据
        MultiValueMap<String, Object> multipartRequestData = new LinkedMultiValueMap<>();
        multipartRequestData.add(FileConstant.fmPostMapping_parsePath_paramName, filePath);
        // 设置请求头部,这里假设服务器接收multipart/form-data类型的数据
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        // 封装请求体
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(multipartRequestData, headers);
        String webUrl = fmUrl + "/" + FileConstant.fmRequestMapping + "/" + FileConstant.fmPostMapping_parsePath;
        // 发送POST请求
        FileRestVo rvo = restTemplate.postForObject(webUrl, requestEntity, FileRestVo.class);
        return rvo ;
    }
    /**
     * 解析文件名称
     * @param fmUrl
     * @param filePaths
     * @return
     */
    public List<FileRestVo> parse(String fmUrl, List<String> filePaths) throws Exception{
        List<FileRestVo> rList = null ;
        if(filePaths != null && filePaths.size() > 0) {
            MultiValueMap<String, Object> multipartRequestData = new LinkedMultiValueMap<>();
            multipartRequestData.add(FileConstant.fmPostMapping_parsePathList_paramName, filePaths);
            // 设置请求头部,这里假设服务器接收multipart/form-data类型的数据
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.MULTIPART_FORM_DATA);
            // 封装请求体
            HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(multipartRequestData, headers);
            String webUrl = fmUrl + "/" + FileConstant.fmRequestMapping + "/" + FileConstant.fmPostMapping_parsePathList;
            // 发送POST请求
            FileRestVo[] rvos = restTemplate.postForObject(webUrl, requestEntity, FileRestVo[].class);
            rList = Arrays.asList(rvos) ;
        }
        return rList ;
    }
    /**
     * 解析文件哈希值
     * @param fmUrl
     * @param hashcode
     * @return
     */
    public FileRestVo parseHashcode(String fmUrl, Integer hashcode){
        // 准备请求数据
        MultiValueMap<String, Object> multipartRequestData = new LinkedMultiValueMap<>();
        multipartRequestData.add(FileConstant.fmPostMapping_parseHashcode_paramName, hashcode);
        // 设置请求头部,这里假设服务器接收multipart/form-data类型的数据
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        // 封装请求体
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(multipartRequestData, headers);
        String webUrl = fmUrl + "/" + FileConstant.fmRequestMapping + "/" + FileConstant.fmPostMapping_parseHashcode;
        // 发送POST请求
        FileRestVo rvo = restTemplate.postForObject(webUrl, requestEntity, FileRestVo.class);
        return rvo ;
    }
    /**
     * 解析文件文件路径中的哈希值并返回图片完整路径
     * @param fmUrl
     * @param filePath
     * @return
     */
    public String getFilePath(String fmUrl, String filePath){
        FileRestVo rvo = null ;
        if(filePath != null && !filePath.trim().equals("")){
            String[] strs = filePath.split("\\?") ;
            String hashValStr = strs[strs.length - 1] ;
            if(hashValStr != null && !hashValStr.trim().equals("") && NumUtil.isPlusIntNumber(hashValStr)){
                int hashVal = Integer.valueOf(hashValStr) ;
                rvo = parseHashcode(fmUrl, hashVal) ;
            }
        }
        if(rvo != null){
            return rvo.getFileWebUrl() + filePath ;
        }
        return null ;
    }
}
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/dyFile/FileRestVo.java
New file
@@ -0,0 +1,44 @@
package com.dy.pmsGlobal.dyFile;
import lombok.Data;
@Data
public class FileRestVo {
    public String fileName ; //生成的文件名称,例如20170818153254_100000007.jpg
    public Integer fileNameHash ; //文件名称的哈希值
    public String fileSysId; //文件名称的哈希值对应的文件系统的id,在配置文件中配置
    public String fileSysAbsolutePath; //文件名称的哈希值对应的文件最终存储绝对路径中的根目录,在配置文件中配置
    public String fileSysRelativePath; //文件名称的哈希值对应的文件最终存储相对路径的目录,在配置文件中配置
    public String fileSysRestUrl; //文件名称的哈希值对应的文件系统的restful URL,在配置文件中配置
    public String fileWebPath; //文件名称的哈希值对应的文件系统的下载文件的web path,动态生成
    public String fileWebUrl; //文件名称的哈希值对应的文件系统的下载文件的web URL,在配置文件中配置
    public String fileWebDownloadUrl; //文件名称的哈希值对应的文件系统的Action下载文件的web URL,在配置文件中配置
    public String toString(){
        return "fileName=" + fileName + "\n"
                + "fileNameHash=" + fileNameHash + "\n"
                + "fileSysId=" + fileSysId + "\n"
                + "fileSysAbsolutePath=" + fileSysAbsolutePath + "\n"
                + "fileSysRelativePath=" + fileSysRelativePath + "\n"
                + "fileSysRestUrl=" + fileSysRestUrl + "\n"
                + "fileWebPath=" + fileWebPath + "\n"
                + "fileWebUrl=" + fileWebUrl + "\n"
                + "fileWebDownloadUrl=" + fileWebDownloadUrl;
    }
    /**
     * 重新创建fileSysWebUrl
     * @param relativeFilePath
     * @param hashcode
     */
    public void createFilePath(String relativeFilePath, Integer hashcode){
        if(relativeFilePath != null){
            if(relativeFilePath.indexOf("?") < 0){
                this.fileWebPath = relativeFilePath + "?" + hashcode;
            }
        }
    }
}
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/dyFile/FileVo.java
New file
@@ -0,0 +1,29 @@
package com.dy.pmsGlobal.dyFile;
import lombok.Data;
@Data
public class FileVo {
    public Long id ; //数据库实体主键
    public Integer hash ;//文件名hash
    public String orgName ;//文件原名
    public String extName ;//文件扩展名
    public String webPath ;//web文件访问路径
    public String webPathZip;//照片文件缩略图访问路径,其他类型文件此属性为null
    public FileVo(){}
    public FileVo(Long id,
                  Integer hash,
                  String orgName,
                  String extName,
                  String webPath,
                  String webPathZip){
        this.id = id ;
        this.hash = hash ;
        this.orgName = orgName ;
        this.extName = extName ;
        this.webPath = webPath ;
        this.webPathZip = webPathZip ;
    }
}
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/dyFile/NameValue.java
New file
@@ -0,0 +1,26 @@
package com.dy.pmsGlobal.dyFile;
public class NameValue {
    public String name ;
    public String value ;
    public NameValue(){
    }
    public NameValue(String name, String value){
        this.name = name ;
        this.value = value ;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getValue() {
        return value;
    }
    public void setValue(String value) {
        this.value = value;
    }
}
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/global/WebFileCtrl.java
New file
@@ -0,0 +1,200 @@
package com.dy.pmsGlobal.global;
import com.dy.common.aop.SsoPowerAop;
import com.dy.common.webUtil.BaseResponse;
import com.dy.common.webUtil.BaseResponseUtils;
import com.dy.pmsGlobal.dyFile.FileConstant;
import com.dy.pmsGlobal.dyFile.FileOperate;
import com.dy.pmsGlobal.dyFile.FileRestVo;
import com.dy.pmsGlobal.dyFile.FileVo;
import com.dy.pmsGlobal.pojoOth.OthFile;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
/**
 * web文件上传示例
 */
@Slf4j
@RestController
@RequestMapping(path="webFile")
@SuppressWarnings("unchecked")//java版本越高,对泛型约束越严,所以配置SuppressWarnings("unchecked"
public class WebFileCtrl {
    @Autowired
    private FileOperate fileOp ;
    @Autowired
    private WebFileSv sv ;
    @Value("${dy.webFile.fmUrl}")
    private String fmUrl ;
    @PostMapping("/upPhoto")
    @SsoPowerAop(power = "-1") //登录与权限同时验证
    //https://blog.zhengru.top/posts/33486.html#%E5%8D%95%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0
    public BaseResponse<?> upPhoto(MultipartFile file, HttpServletRequest req) {
        try {
            if (file != null) {
                String[] fileNameGrp = fileOp.splitFileName(file) ;
                if(fileNameGrp != null && fileNameGrp[0] != null && fileNameGrp[1] != null){
                    if(!fileNameGrp[1].trim().equals("")){
                        FileRestVo frVo = fileOp.saveFile(file,
                                fmUrl,
                                FileConstant.fileRequestMapping,
                                FileConstant.filePostMapping_photo,
                                FileConstant.NotRegionNum,
                                fileNameGrp[1],
                                null);
                        String fileMainName = fileOp.getFileMainName(frVo.fileName) ;
                        Long id = this.saveFileInfo(fileNameGrp[0], fileNameGrp[1], fileMainName, frVo.fileNameHash, frVo.fileWebPath);
                        FileVo fvo = new FileVo(id, frVo.fileNameHash, fileNameGrp[0], fileNameGrp[1], (frVo.getFileWebUrl() + frVo.getFileWebPath()), fileOp.getImgFileZipPath(frVo.getFileWebUrl() + frVo.getFileWebPath())) ;
                        return  BaseResponseUtils.buildSuccess(fvo) ;
                    }else {
                        return BaseResponseUtils.buildError("未得到上传文件的扩展名");
                    }
                }else {
                    return BaseResponseUtils.buildError("未能拆分上传文件名称");
                }
            } else {
                return BaseResponseUtils.buildError("未上传文件");
            }
        } catch (Exception e) {
            log.error("上传照片文件异常", e);
            return BaseResponseUtils.buildException(e.getMessage());
        }
    }
    @PostMapping("/upPhone")
    @SsoPowerAop(power = "-1")
    public BaseResponse<?> upPhone(MultipartFile file, HttpServletRequest req) {
        try {
            if (file != null) {
                String[] fileNameGrp = fileOp.splitFileName(file) ;
                if(fileNameGrp != null && fileNameGrp[0] != null && fileNameGrp[1] != null){
                    if(!fileNameGrp[1].trim().equals("")){
                        FileRestVo frVo = fileOp.saveFile(file,
                                fmUrl,
                                FileConstant.fileRequestMapping,
                                FileConstant.filePostMapping_phone,
                                FileConstant.NotRegionNum,
                                fileNameGrp[1],
                                null);
                        String fileMainName = fileOp.getFileMainName(frVo.fileName) ;
                        Long id = this.saveFileInfo(fileNameGrp[0], fileNameGrp[1], fileMainName, frVo.fileNameHash, frVo.fileWebPath);
                        FileVo fvo = new FileVo(id, frVo.fileNameHash, fileNameGrp[0], fileNameGrp[1],frVo.getFileWebUrl() + frVo.getFileWebPath(), null) ;
                        return  BaseResponseUtils.buildSuccess(fvo) ;
                    }else {
                        return BaseResponseUtils.buildError("未得到上传文件的扩展名");
                    }
                }else {
                    return BaseResponseUtils.buildError("未能拆分上传文件名称");
                }
            } else {
                return BaseResponseUtils.buildError("未上传文件");
            }
        } catch (Exception e) {
            log.error("上传照片文件异常", e);
            return BaseResponseUtils.buildException(e.getMessage());
        }
    }
    @PostMapping("/upVideo")
    @SsoPowerAop(power = "-1")
    public BaseResponse<?> upVideo(MultipartFile file, HttpServletRequest req) {
        try {
            if (file != null) {
                String[] fileNameGrp = fileOp.splitFileName(file) ;
                if(fileNameGrp != null && fileNameGrp[0] != null && fileNameGrp[1] != null){
                    if(!fileNameGrp[1].trim().equals("")){
                        FileRestVo frVo = fileOp.saveFile(file,
                                fmUrl,
                                FileConstant.fileRequestMapping,
                                FileConstant.filePostMapping_video,
                                FileConstant.NotRegionNum,
                                fileNameGrp[1],
                                null);
                        String fileMainName = fileOp.getFileMainName(frVo.fileName) ;
                        Long id = this.saveFileInfo(fileNameGrp[0], fileNameGrp[1], fileMainName, frVo.fileNameHash, frVo.fileWebPath);
                        FileVo fvo = new FileVo(id, frVo.fileNameHash, fileNameGrp[0], fileNameGrp[1],frVo.getFileWebUrl() + frVo.getFileWebPath(), null) ;
                        return  BaseResponseUtils.buildSuccess(fvo) ;
                    }else {
                        return BaseResponseUtils.buildError("未得到上传文件的扩展名");
                    }
                }else {
                    return BaseResponseUtils.buildError("未能拆分上传文件名称");
                }
            } else {
                return BaseResponseUtils.buildError("未上传文件");
            }
        } catch (Exception e) {
            log.error("上传照片文件异常", e);
            return BaseResponseUtils.buildException(e.getMessage());
        }
    }
    @PostMapping("/upDocument")
    @SsoPowerAop(power = "-1")
    public BaseResponse<?> upDocument(MultipartFile file, HttpServletRequest req) {
        try {
            if (file != null) {
                String[] fileNameGrp = fileOp.splitFileName(file) ;
                if(fileNameGrp != null && fileNameGrp[0] != null && fileNameGrp[1] != null){
                    if(!fileNameGrp[1].trim().equals("")){
                        FileRestVo frVo = fileOp.saveFile(file,
                                fmUrl,
                                FileConstant.fileRequestMapping,
                                FileConstant.filePostMapping_document,
                                FileConstant.NotRegionNum,
                                fileNameGrp[1],
                                null);
                        String fileMainName = fileOp.getFileMainName(frVo.fileName) ;
                        Long id = this.saveFileInfo(fileNameGrp[0], fileNameGrp[1], fileMainName, frVo.fileNameHash, frVo.fileWebPath);
                        FileVo fvo = new FileVo(id, frVo.fileNameHash, fileNameGrp[0], fileNameGrp[1],frVo.getFileWebUrl() + frVo.getFileWebPath(), null) ;
                        return  BaseResponseUtils.buildSuccess(fvo) ;
                    }else {
                        return BaseResponseUtils.buildError("未得到上传文件的扩展名");
                    }
                }else {
                    return BaseResponseUtils.buildError("未能拆分上传文件名称");
                }
            } else {
                return BaseResponseUtils.buildError("未上传文件");
            }
        } catch (Exception e) {
            log.error("上传照片文件异常", e);
            return BaseResponseUtils.buildException(e.getMessage());
        }
    }
    /**
     * 数据库存储
     * @param orgName 文件原名称
     * @param extName 文件扩展名
     * @param newName 文件新名称
     * @param hash  文件新名称的哈希值
     * @param filePath 文件服务端存储相对路径
     * @return
     */
    private Long saveFileInfo(String orgName, String extName, String newName, Integer hash, String filePath){
        OthFile po = new OthFile() ;
        po.orgName = orgName ;
        po.extName = extName ;
        po.newName = newName ;
        po.hash = hash ;
        po.filePath = filePath ;
        return this.sv.save(po) ;
    }
}
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/global/WebFileSv.java
New file
@@ -0,0 +1,21 @@
package com.dy.pmsGlobal.global;
import com.dy.pmsGlobal.daoOth.OthFileMapper;
import com.dy.pmsGlobal.pojoOth.OthFile;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@Service
public class WebFileSv {
    @Autowired
    private OthFileMapper dao;
    @Transactional
    public Long save(OthFile po){
        this.dao.insertSelective(po) ;
        return po.id ;
    }
}
pms-parent/pms-global/src/main/java/com/dy/pmsGlobal/pojoOth/OthFile.java
New file
@@ -0,0 +1,50 @@
package com.dy.pmsGlobal.pojoOth;
import com.baomidou.mybatisplus.annotation.TableName;
import com.dy.common.po.BaseEntity;
import lombok.*;
/**
 * 上载的文件信息
 */
//2024-04-12下面TableName不用配置表名称(value="BaUser"或“ba_user”)
//只要通过驼峰命名法则类名与表名对应起来就可以了,如果不能对应起来,需要指定表名称
//例如@TableName(value="TestUser" autoResultMap = true)
@TableName(value="ba_role", autoResultMap = true)
@Data
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class OthFile implements BaseEntity {
    /**
    * 主键
    */
    public Long id;
    /**
    * 文件原名称
    */
    public String orgName;
    /**
    * 扩展名
    */
    public String extName;
    /**
    * 上传文件后系统自动给文件赋的新名称
    */
    public String newName;
    /**
    * 文件hash值
    */
    public Integer hash;
    /**
    * 文件在服务端存储的相对路径
    */
    public String filePath;
}
pms-parent/pms-global/src/main/resources/application-global.yml
@@ -1,3 +1,9 @@
spring:
    servlet:
        multipart:
            # 前端上传文件,限制单个文件的大小和限制所有文件的大小
            max-file-size: 1MB
            max-request-size: 10MB
logging:
    charset:
        console: UTF-8
@@ -36,12 +42,15 @@
        lazy-load-trigger-methods: ""  # 阻挡不相干的操作触发,实现懒加载
        cache-enabled: true  #打开全局缓存开关(二级环境),默认值是true
        #default-enum-type-handler: com.dy.common.mybatis.envm.EnumCodeTypeHandler
pms:
    global:
        dev: true   #是否开发阶段,true或false,开发阶段不进行登录验证及权限验证
        ssoUserExpireAfterAccess: 60  #sso登录后,用户在一定时间(ssoUserExpireAfterAccess)(分钟)内未访问系统,系统清除缓存信息,使用重新登录系统
        ssoCacheSizeInit: 10  #sso缓存初始容量
        ssoCacheSizeMax: 10000  #sso缓存最大容量,即支持ssoCacheSizeMax个用户同时登录
    nginx:
        webPort: 8000
    sso:
        checkUrl: http://127.0.0.1:12344/sso/sso/ssoCheck
        webPort: 12344
@@ -58,6 +67,144 @@
    other:
        webPort: 12348
        idSuffix: 5
    file:
        webPort: 12349
        idSuffix: 6
        idSuffix: 99
    file1:
        webPort: 12380
    file2:
        webPort: 12380
    file3:
        webPort: 12380
    file4:
        webPort: 12380
    file5:
        webPort: 12380
    file6:
        webPort: 12380
    file7:
        webPort: 12380
    file8:
        webPort: 12380
    file9:
        webPort: 12380
    file10:
        webPort: 12380
    file11:
        webPort: 12380
    file12:
        webPort: 12380
#web分布式文件系统
dy:
    photoZipWidth: 400 #缩略图尺寸
    webFile:
        fmUrl: http://127.0.0.1:${pms.other.webPort}/other # fm的web上下文 URL
        sv1:
            id: dyFile1
            absolutePath: E:/java/nginx-1.24.0/html/webfiles/
            relativePath: webFile1
            hashStart: 0
            hashEnd: 5461
            restUrl: http://127.0.0.1:${pms.file1.webPort}/file #file是上下文
            webUrl: http://127.0.0.1:${pms.nginx.webPort}/webfiles/ #nginx服务路径
            webDownloadUrl: http://127.0.0.1:${pms.file1.webPort}/file/download/down
        sv2:
            id: dyFile2
            absolutePath: E:/java/nginx-1.24.0/html/webfiles/
            relativePath: webFile2
            hashStart: 5462
            hashEnd: 10923
            restUrl: http://127.0.0.1:${pms.file2.webPort}/file
            webUrl: http://127.0.0.1:${pms.nginx.webPort}/webfiles/
            webDownloadUrl: http://127.0.0.1:${pms.file2.webPort}/file/download/down
        sv3:
            id: dyFile3
            absolutePath: E:/java/nginx-1.24.0/html/webfiles/
            relativePath: webFile3
            hashStart: 10924
            hashEnd: 16385
            restUrl: http://127.0.0.1:${pms.file3.webPort}/file
            webUrl: http://127.0.0.1:${pms.nginx.webPort}/webfiles/
            webDownloadUrl: http://127.0.0.1:${pms.file3.webPort}/file/download/down
        sv4:
            id: dyFile4
            absolutePath: E:/java/nginx-1.24.0/html/webfiles/
            relativePath: webFile4
            hashStart: 16386
            hashEnd: 21847
            restUrl: http://127.0.0.1:${pms.file4.webPort}/file
            webUrl: http://127.0.0.1:${pms.nginx.webPort}/webfiles/
            webDownloadUrl: http://127.0.0.1:${pms.file4.webPort}/file/download/down
        sv5:
            id: dyFile5
            absolutePath: E:/java/nginx-1.24.0/html/webfiles/
            relativePath: webFile5
            hashStart: 21848
            hashEnd: 27309
            restUrl: http://127.0.0.1:${pms.file5.webPort}/file
            webUrl: http://127.0.0.1:${pms.nginx.webPort}/webfiles/
            webDownloadUrl: http://127.0.0.1:${pms.file5.webPort}/file/download/down
        sv6:
            id: dyFile6
            absolutePath: E:/java/nginx-1.24.0/html/webfiles/
            relativePath: webFile6
            hashStart: 27310
            hashEnd: 32767
            restUrl: http://127.0.0.1:${pms.file6.webPort}/file
            webUrl: http://127.0.0.1:${pms.nginx.webPort}/webfiles/
            webDownloadUrl: http://127.0.0.1:${pms.file6.webPort}/file/download/down
        sv7:
            id: dyFile7
            absolutePath: E:/java/nginx-1.24.0/html/webfiles/
            relativePath: webFile7
            hashStart: 32768
            hashEnd: 38229
            restUrl: http://127.0.0.1:${pms.file7.webPort}/file
            webUrl: http://127.0.0.1:${pms.nginx.webPort}/webfiles/
            webDownloadUrl: http://127.0.0.1:${pms.file7.webPort}/file/download/down
        sv8:
            id: dyFile8
            absolutePath: E:/java/nginx-1.24.0/html/webfiles/
            relativePath: webFile8
            hashStart: 38230
            hashEnd: 43691
            restUrl: http://127.0.0.1:${pms.file8.webPort}/file
            webUrl: http://127.0.0.1:${pms.nginx.webPort}/webfiles/
            webDownloadUrl: http://127.0.0.1:${pms.file8.webPort}/file/download/down
        sv9:
            id: dyFile9
            absolutePath: E:/java/nginx-1.24.0/html/webfiles/
            relativePath: webFile9
            hashStart: 43692
            hashEnd: 49153
            restUrl: http://127.0.0.1:${pms.file9.webPort}/file
            webUrl: http://127.0.0.1:${pms.nginx.webPort}/webfiles/
            webDownloadUrl: http://127.0.0.1:${pms.file9.webPort}/file/download/down
        sv10:
            id: dyFile10
            absolutePath: E:/java/nginx-1.24.0/html/webfiles/
            relativePath: webFile10
            hashStart: 49154
            hashEnd: 54615
            restUrl: http://127.0.0.1:${pms.file10.webPort}/file
            webUrl: http://127.0.0.1:${pms.nginx.webPort}/webfiles/
            webDownloadUrl: http://127.0.0.1:${pms.file10.webPort}/file/download/down
        sv11:
            id: dyFile11
            absolutePath: E:/java/nginx-1.24.0/html/webfiles/
            relativePath: webFile11
            hashStart: 54616
            hashEnd: 60077
            restUrl: http://127.0.0.1:${pms.file11.webPort}/file
            webUrl: http://127.0.0.1:${pms.nginx.webPort}/webfiles/
            webDownloadUrl: http://127.0.0.1:${pms.file11.webPort}/file/download/down
        sv12:
            id: dyFile12
            absolutePath: E:/java/nginx-1.24.0/html/webfiles/
            relativePath: webFile12
            hashStart: 60078
            hashEnd: 65535
            restUrl: http://127.0.0.1:${pms.file12.webPort}/file
            webUrl: http://127.0.0.1:${pms.nginx.webPort}/webfiles/
            webDownloadUrl: http://127.0.0.1:${pms.file12.webPort}/file/download/down
pms-parent/pms-global/src/main/resources/mapper/OthFileMapper.xml
New file
@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dy.pmsGlobal.daoOth.OthFileMapper">
  <resultMap id="BaseResultMap" type="com.dy.pmsGlobal.pojoOth.OthFile">
    <!--@mbg.generated-->
    <!--@Table oth_file-->
    <id column="id" property="id" />
    <result column="org_name" property="orgName" />
    <result column="ext_name" property="extName" />
    <result column="new_name" property="newName" />
    <result column="hash" property="hash" />
    <result column="file_path" property="filePath" />
  </resultMap>
  <sql id="Base_Column_List">
    <!--@mbg.generated-->
    id, org_name, ext_name, new_name, hash, file_path
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
    <!--@mbg.generated-->
    select
    <include refid="Base_Column_List" />
    from oth_file
    where id = #{id}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
    <!--@mbg.generated-->
    delete from oth_file
    where id = #{id}
  </delete>
  <insert id="insert" parameterType="com.dy.pmsGlobal.pojoOth.OthFile">
    <!--@mbg.generated-->
    insert into oth_file (id, org_name, ext_name, new_name, hash, file_path)
    values (#{id}, #{orgName}, #{extName}, #{newName}, #{hash}, #{filePath})
  </insert>
  <insert id="insertSelective" parameterType="com.dy.pmsGlobal.pojoOth.OthFile">
    <!--@mbg.generated-->
    insert into oth_file
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="orgName != null">
        org_name,
      </if>
      <if test="extName != null">
        ext_name,
      </if>
      <if test="newName != null">
        new_name,
      </if>
      <if test="hash != null">
        hash,
      </if>
      <if test="filePath != null">
        file_path,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id},
      </if>
      <if test="orgName != null">
        #{orgName},
      </if>
      <if test="extName != null">
        #{extName},
      </if>
      <if test="newName != null">
        #{newName},
      </if>
      <if test="hash != null">
        #{hash},
      </if>
      <if test="filePath != null">
        #{filePath},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.dy.pmsGlobal.pojoOth.OthFile">
    <!--@mbg.generated-->
    update oth_file
    <set>
      <if test="orgName != null">
        org_name = #{orgName},
      </if>
      <if test="extName != null">
        ext_name = #{extName},
      </if>
      <if test="newName != null">
        new_name = #{newName},
      </if>
      <if test="hash != null">
        hash = #{hash},
      </if>
      <if test="filePath != null">
        file_path = #{filePath},
      </if>
    </set>
    where id = #{id}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.dy.pmsGlobal.pojoOth.OthFile">
    <!--@mbg.generated-->
    update oth_file
    set org_name = #{orgName},
      ext_name = #{extName},
      new_name = #{newName},
      hash = #{hash},
      file_path = #{filePath}
    where id = #{id}
  </update>
</mapper>
pms-parent/pms-web-base/src/main/java/com/dy/pmsBase/PmsBaseApplication.java
@@ -9,7 +9,7 @@
@SpringBootApplication
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {"com.dy.common", "com.dy.pmsGlobal", "com.dy.pmsBase"})
@MapperScan(basePackages={"com.dy.pmsGlobal.daoBa"})
@MapperScan(basePackages={"com.dy.pmsGlobal.daoBa","com.dy.pmsGlobal.daoOth"})
public class PmsBaseApplication {
    public static void main(String[] args) {
pms-parent/pms-web-file/pom.xml
@@ -29,6 +29,12 @@
            <artifactId>pms-global</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>net.coobird</groupId>
            <artifactId>thumbnailator</artifactId>
            <version>${thumbnailator.version}</version>
        </dependency>
    </dependencies>
    <build>
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/DyFileApplication.java
File was renamed from pms-parent/pms-web-file/src/main/java/com/dy/pmsFile/PmsFileApplication.java
@@ -1,4 +1,4 @@
package com.dy.pmsFile;
package com.dy.dyFile;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
@@ -8,12 +8,12 @@
@SpringBootApplication
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {"com.dy.common", "com.dy.pmsGlobal", "com.dy.pmsFile"})
@ComponentScan(basePackages = {"com.dy.common", "com.dy.pmsGlobal", "com.dy.dyFile"})
@MapperScan(basePackages={"com.dy.pmsGlobal.daoBa", "com.dy.pmsGlobal.daoOth"})
public class PmsFileApplication {
public class DyFileApplication {
    public static void main(String[] args) {
        SpringApplication.run(PmsFileApplication.class, args);
        SpringApplication.run(DyFileApplication.class, args);
    }
}
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/download/DownloadFileCtr.java
New file
@@ -0,0 +1,18 @@
package com.dy.dyFile.download;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * web文件下载
 */
@Slf4j
@RestController
@RequestMapping(path="download")
@SuppressWarnings("unchecked")//java版本越高,对泛型约束越严,所以配置SuppressWarnings("unchecked")
public class DownloadFileCtr {
    public String down(){
        return null ;
    }
}
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/files/FileCtrl.java
New file
@@ -0,0 +1,196 @@
package com.dy.dyFile.files;
import com.dy.common.util.NumUtil;
import com.dy.dyFile.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.InputStream;
/**
 * web文件上传
 */
@Slf4j
@RestController
@RequestMapping(path="file")
@SuppressWarnings("unchecked")//java版本越高,对泛型约束越严,所以配置SuppressWarnings("unchecked")
public class FileCtrl {
    @Value("${dy.photoZipWidth}")
    private String photoZipWidthStr ;
    /**
     * web分布式文件系统保存照片文件
     * @param file
     * @param regionNum
     * @param json 水印信息
     * @param absolutePath
     * @param relativePath
     * @param fileName
     * @return
     */
    @PostMapping("/savePhoto")
    public String savePhoto(MultipartFile file,
                          String regionNum,
                          String json,
                          String absolutePath,
                          String relativePath,
                          String fileName) {
        Integer photoZipWidth = 400 ;
        if(photoZipWidthStr != null && NumUtil.isPlusIntNumber(photoZipWidthStr)){
            photoZipWidth = Integer.parseInt(photoZipWidthStr) ;
        }
        String fileRelativePath = null ;
        try {
            if (file != null) {
                if(absolutePath != null && relativePath != null && fileName != null){
                    InputStream input = file.getInputStream() ;
                    //生成新文件相对路径
                    FilePhotoUtil fUtil = new FilePhotoUtil() ;
                    fileRelativePath = fUtil.newFileRelativityPath(absolutePath, relativePath, regionNum) + fileName ;
                    String filePath = absolutePath + fileRelativePath ;
                    if(!fUtil.saveFile(filePath, input)){
                        fileRelativePath = null ;
                    }else {
                        //存储成功
                        File fPic = new File(filePath) ;
                        //生成缩略图
                        int index = filePath.lastIndexOf(".") ;
                        String zipFilePath1 = filePath.substring(0, index) ;
                        String zipFilePath2 = filePath.substring(index) ;
                        String zipFilePath = zipFilePath1 + "_" + zipFilePath2 ;
                        InputStream zipFileInput = null ;
                        if(zipFilePath2.equalsIgnoreCase(".png")){
                            zipFileInput = ZipImg.zipToPng(fPic, photoZipWidth, photoZipWidth) ;
                        }else{
                            zipFileInput = ZipImg.zipToJpg(fPic, photoZipWidth, photoZipWidth) ;
                        }
                        boolean zipOk = new FileUtil().saveFile(zipFilePath, zipFileInput) ;
                    }
                }
            }
        } catch (Exception e) {
            log.error("保存照片文件异常", e);
        }
        return fileRelativePath ;
    }
    /**
     * web分布式文件系统保存录音音频文件
     * @param file
     * @param regionNum
     * @param json 水印信息
     * @param absolutePath
     * @param relativePath
     * @param fileName
     * @return
     */
    @PostMapping("/savePhone")
    public String savePhone(MultipartFile file,
                            String regionNum,
                            String json,
                            String absolutePath,
                            String relativePath,
                            String fileName) {
        String fileRelativePath = null ;
        try {
            if (file != null) {
                if(absolutePath != null && relativePath != null && fileName != null){
                    InputStream input = file.getInputStream() ;
                    //生成新文件相对路径
                    FilePhoneUtil fUtil = new FilePhoneUtil() ;
                    fileRelativePath = fUtil.newFileRelativityPath(absolutePath, relativePath, regionNum) + fileName ;
                    String filePath = absolutePath + fileRelativePath ;
                    if(!fUtil.saveFile(filePath, input)){
                        fileRelativePath = null ;
                    }
                }
            }
        } catch (Exception e) {
            log.error("保存录音音频文件异常", e);
        }
        return fileRelativePath ;
    }
    /**
     * web分布式文件系统保存录像视频文件
     * @param file
     * @param regionNum
     * @param json 水印信息
     * @param absolutePath
     * @param relativePath
     * @param fileName
     * @return
     */
    @PostMapping("/saveVideo")
    public String saveVideo(MultipartFile file,
                            String regionNum,
                            String json,
                            String absolutePath,
                            String relativePath,
                            String fileName) {
        String fileRelativePath = null ;
        try {
            if (file != null) {
                if(absolutePath != null && relativePath != null && fileName != null){
                    InputStream input = file.getInputStream() ;
                    //生成新文件相对路径
                    FileVideoUtil fUtil = new FileVideoUtil() ;
                    fileRelativePath = fUtil.newFileRelativityPath(absolutePath, relativePath, regionNum) + fileName ;
                    String filePath = absolutePath + fileRelativePath ;
                    if(!fUtil.saveFile(filePath, input)){
                        fileRelativePath = null ;
                    }
                }
            }
        } catch (Exception e) {
            log.error("保存录像视频文件异常", e);
        }
        return fileRelativePath ;
    }
    /**
     * web分布式文件系统保存文档文件
     * @param file
     * @param regionNum
     * @param json 水印信息
     * @param absolutePath
     * @param relativePath
     * @param fileName
     * @return
     */
    @PostMapping("/saveDocument")
    public String saveDocument(MultipartFile file,
                            String regionNum,
                            String json,
                            String absolutePath,
                            String relativePath,
                            String fileName) {
        String fileRelativePath = null ;
        try {
            if (file != null) {
                if(absolutePath != null && relativePath != null && fileName != null){
                    InputStream input = file.getInputStream() ;
                    //生成新文件相对路径
                    FileDocumentUtil fUtil = new FileDocumentUtil() ;
                    fileRelativePath = fUtil.newFileRelativityPath(absolutePath, relativePath, regionNum) + fileName ;
                    String filePath = absolutePath + fileRelativePath ;
                    if(!fUtil.saveFile(filePath, input)){
                        fileRelativePath = null ;
                    }
                }
            }
        } catch (Exception e) {
            log.error("保存文档文件异常", e);
        }
        return fileRelativePath ;
    }
}
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/util/FileConstant.java
New file
@@ -0,0 +1,13 @@
package com.dy.dyFile.util;
public class FileConstant {
    public static final String YES = "1" ;
    public static final String NO = "0" ;
    public static final String phoneFileBasePath = "/phone/" ;//录音文件
    public static final String photoFileBasePath = "/photo/" ;//照片文件
    public static final String videoFileBasePath = "/video/" ;//视频文件
    public static final String iconFileBasePath = "/icon/" ;//头像文件
    public static final String documentFileBasePath = "/document/" ;//文档文件
}
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/util/FileDocumentUtil.java
New file
@@ -0,0 +1,46 @@
package com.dy.dyFile.util;
import com.dy.common.util.DateTime;
import com.dy.pmsGlobal.dyFile.FileConstant;
import java.io.File;
/**
 * 文档类
 * @author Administrator
 *
 */
public class FileDocumentUtil extends FileUtil {
    /**
     * 生成文件相对路径名称
     *
     * @param absolutePath
     * @param relativePath
     * @param regionNum
     * @return
     */
    public String newFileRelativityPath(String absolutePath, String relativePath, String regionNum) {
        String relativityPath = relativePath ;
        if(regionNum != null && !regionNum.trim().equals("") && !regionNum.trim().equals(FileConstant.NotRegionNum)){
            relativityPath += regionNum ;
        }else{
            if(com.dy.dyFile.util.FileConstant.documentFileBasePath.startsWith("/")){
                if(relativityPath.endsWith("/")){
                    relativityPath = relativityPath.substring(0, relativityPath.length() - 1) ;
                }
            }
        }
        relativityPath += com.dy.dyFile.util.FileConstant.documentFileBasePath + DateTime.yyyyMMdd() + "/";
        String basePath = absolutePath + relativityPath;
        File dir = new File(basePath);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return relativityPath;
    }
}
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/util/FileIconUtil.java
New file
@@ -0,0 +1,38 @@
package com.dy.dyFile.util;
import com.dy.pmsGlobal.dyFile.FileConstant;
import java.io.File;
public class FileIconUtil extends FileUtil {
    /**
     * 生成文件相对路径名称
     *
     * @param absolutePath
     * @param relativePath
     * @param regionNum
     * @return
     */
    public String newFileRelativityPath(String absolutePath, String relativePath, String regionNum) {
        String relativityPath = relativePath ;
        if(regionNum != null && !regionNum.trim().equals("") && !regionNum.trim().equals(FileConstant.NotRegionNum)){
            relativityPath += regionNum ;
        }else{
            if(com.dy.dyFile.util.FileConstant.iconFileBasePath.startsWith("/")){
                if(relativityPath.endsWith("/")){
                    relativityPath = relativityPath.substring(0, relativityPath.length() - 1) ;
                }
            }
        }
        relativityPath += com.dy.dyFile.util.FileConstant.iconFileBasePath;
        String basePath = absolutePath + relativityPath;
        File dir = new File(basePath);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return relativityPath;
    }
}
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/util/FilePhoneUtil.java
New file
@@ -0,0 +1,41 @@
package com.dy.dyFile.util;
import com.dy.common.util.DateTime;
import com.dy.pmsGlobal.dyFile.FileConstant;
import java.io.File;
public class FilePhoneUtil extends FileUtil {
    /**
     * 生成文件相对路径名称
     *
     * @param absolutePath
     * @param relativePath
     * @param regionNum
     * @return
     */
    public String newFileRelativityPath(String absolutePath, String relativePath, String regionNum) {
        String relativityPath = relativePath ;
        if(regionNum != null && !regionNum.trim().equals("") && !regionNum.trim().equals(FileConstant.NotRegionNum)){
            relativityPath += regionNum ;
        }else{
            if(com.dy.dyFile.util.FileConstant.phoneFileBasePath.startsWith("/")){
                if(relativityPath.endsWith("/")){
                    relativityPath = relativityPath.substring(0, relativityPath.length() - 1) ;
                }
            }
        }
        relativityPath += com.dy.dyFile.util.FileConstant.phoneFileBasePath + DateTime.yyyyMMdd() + "/";
        String basePath = absolutePath + relativityPath;
        File dir = new File(basePath);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return relativityPath;
    }
}
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/util/FilePhotoUtil.java
New file
@@ -0,0 +1,39 @@
package com.dy.dyFile.util;
import com.dy.common.util.DateTime;
import com.dy.pmsGlobal.dyFile.FileConstant;
import java.io.File;
public class FilePhotoUtil extends FileUtil {
    /**
     * 生成文件相对路径名称
     *
     * @param absolutePath
     * @param relativePath
     * @param regionNum
     * @return
     */
    public String newFileRelativityPath(String absolutePath, String relativePath, String regionNum) {
        String relativityPath = relativePath ;
        if(regionNum != null && !regionNum.trim().equals("") && !regionNum.trim().equals(FileConstant.NotRegionNum)){
            relativityPath += ("-"  + regionNum) ;
        }else{
            if(com.dy.dyFile.util.FileConstant.photoFileBasePath.startsWith("/")){
                if(relativityPath.endsWith("/")){
                    relativityPath = relativityPath.substring(0, relativityPath.length() - 1) ;
                }
            }
        }
        relativityPath += com.dy.dyFile.util.FileConstant.photoFileBasePath + DateTime.yyyyMMdd() + "/";
        String filePath = absolutePath + relativityPath;
        File dir = new File(filePath);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return relativityPath;
    }
}
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/util/FileUtil.java
New file
@@ -0,0 +1,122 @@
package com.dy.dyFile.util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class FileUtil {
    protected static final Logger log = LogManager.getLogger(FileUtil.class.getName()) ;
    /**
     * 保存文件
     * @param fileAbsolutelyPath 文件绝对路径
     * @param input
     * @return 保存文件的是否成功的结果
     */
    public boolean saveFile(String fileAbsolutelyPath , InputStream input){
        boolean success = true ;
        if(fileAbsolutelyPath == null || input == null){
            success = false ;
        }else{
            OutputStream os = null;
            try {
                os = new FileOutputStream(fileAbsolutelyPath);
                byte[] b = new byte[1024] ;
                int len = input.read(b) ;
                while(len > 0){
                    os.write(b, 0, len);
                    len = input.read(b) ;
                    os.flush();
                }
            } catch (Exception e) {
                e.printStackTrace();
                success = false ;
            } finally {
                try {
                    if(os != null){
                        os.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return success ;
    }
    /**
     * 复制文件
     * @param fromFile
     * @param realPath 绝对路径
     * @param newFileRelativityPath 相对路径
     * @return
     */
    public boolean copyFile(File fromFile, String realPath, String newFileRelativityPath){
        boolean flag = true ;
        FileChannel fromChannel = null ;
        FileChannel toChannel = null ;
        try {
            fromChannel = new RandomAccessFile(fromFile, "r").getChannel();
            toChannel = new RandomAccessFile(new File(realPath + newFileRelativityPath), "rw").getChannel();
            fromChannel.transferTo(0, fromChannel.size(), toChannel);
        } catch (FileNotFoundException e) {
            flag = false ;
            log.error("复制文件出错:" + e.getMessage()) ;
        } catch (IOException e) {
            flag = false ;
            log.error("复制文件出错:" + e.getMessage()) ;
        }finally{
            try{
                if(fromChannel != null){
                    fromChannel.close() ;
                }
                if(toChannel != null){
                    toChannel.close() ;
                }
            }catch(Exception e){}finally{}
        }
        return flag ;
    }
    /**
     * 删除文件
     * @param f
     */
    public boolean deleteFile(File f){
        boolean flag = false ;
        try{
            if(f != null){
                flag = f.delete() ;
            }
        }catch(Exception e){
        }finally{}
        return flag ;
    }
    /**
     * 删除文件
     * @param path
     */
    public boolean deleteFile(String path){
        boolean flag = false ;
        try{
            if(path != null && !path.equals("")){
                File f = new File(path) ;
                if(f != null && f.exists()){
                    flag = f.delete() ;
                }
            }
        }catch(Exception e){
        }finally{}
        return flag ;
    }
}
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/util/FileVideoUtil.java
New file
@@ -0,0 +1,39 @@
package com.dy.dyFile.util;
import com.dy.common.util.DateTime;
import com.dy.pmsGlobal.dyFile.FileConstant;
import java.io.File;
public class FileVideoUtil extends FileUtil {
    /**
     * 生成文件相对路径名称
     *
     * @param absolutePath
     * @param relativePath
     * @param regionNum
     * @return
     */
    public String newFileRelativityPath(String absolutePath, String relativePath, String regionNum) {
        String relativityPath = relativePath ;
        if(regionNum != null && !regionNum.trim().equals("") && !regionNum.trim().equals(FileConstant.NotRegionNum)){
            relativityPath += regionNum ;
        }else{
            if(com.dy.dyFile.util.FileConstant.videoFileBasePath.startsWith("/")){
                if(relativityPath.endsWith("/")){
                    relativityPath = relativityPath.substring(0, relativityPath.length() - 1) ;
                }
            }
        }
        relativityPath += com.dy.dyFile.util.FileConstant.videoFileBasePath + DateTime.yyyyMMdd() + "/";
        String basePath = absolutePath + relativityPath;
        File dir = new File(basePath);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return relativityPath;
    }
}
pms-parent/pms-web-file/src/main/java/com/dy/dyFile/util/ZipImg.java
New file
@@ -0,0 +1,63 @@
package com.dy.dyFile.util;
import net.coobird.thumbnailator.Thumbnails;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
/**
 * 生成图片的缩略图
 */
public class ZipImg {
    public static InputStream zipToJpg(File file, int xSize, int ySize) throws Exception{
        return zip(file, "jpg", xSize, ySize) ;
    }
    public static InputStream zipToPng(File file, int xSize, int ySize) throws Exception{
        return zip(file, "png", xSize, ySize) ;
    }
    public static void zipToFile(File file, File toFile, int xSize, int ySize) throws Exception{
        Thumbnails.of(file).size(xSize, ySize).outputQuality(0.5f).toFile(toFile);//0f-1f 质量越高
    }
    private static InputStream zip(File file, String type, int xSize, int ySize) throws Exception{
        BufferedImage bi = Thumbnails.of(file).size(xSize, ySize).outputQuality(1f).asBufferedImage();
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(bi, type, os);
        InputStream in = new ByteArrayInputStream(os.toByteArray());
        bi = null ;
        os = null ;
        return in ;
    }
    public static void main(String[] args){
        File f = new File("D:/test.jpg") ;
        String name = f.getName() ;
        String path = f.getPath() ;
        System.out.println(name);
        System.out.println(path);
        int index = path.lastIndexOf(".") ;
        String s1 = path.substring(0, index) ;
        String s2 = path.substring(index) ;
        System.out.println(s1);
        System.out.println(s2);
        String newFilePath = s1 + "_" + s2 ;
        try {
            InputStream input = zipToJpg(f, 400, 400) ;
            new FileUtil().saveFile(newFilePath, input) ;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
pms-parent/pms-web-file/src/main/resources/application.yml
@@ -4,7 +4,7 @@
#web服务端口,tomcat默认是8080
server:
    port: ${pms.file.webPort}
    port: ${pms.file1.webPort}
    servlet:
        context-path: /file #web访问上下文路径
        context-parameters:
pms-parent/pms-web-other/src/main/java/com/dy/pmsOther/dyFm/DyFileSvConf.java
New file
@@ -0,0 +1,159 @@
package com.dy.pmsOther.dyFm;
import java.util.ArrayList;
import java.util.List;
public class DyFileSvConf {
    public static class Group{
        public List<Vo> list ;
        public Group(){
            this.list = new ArrayList<Vo>() ;
        }
        public void add(Vo vo)throws Exception{
            if(vo == null){
                throw new Exception("出错,预加入集合Vo为空了!") ;
            }
            if(list.size() > 0){
                for(Vo lvo : list){
                    lvo.checkEqual(vo) ;
                }
                this.list.add(vo) ;
            }else{
                this.list.add(vo) ;
            }
        }
        public void check() throws Exception{
            Integer startV = 0 ;
            doCheck(startV) ;
        }
        private void doCheck(Integer startV) throws Exception{
            boolean find = false ;
            for(Vo vo : list){
                if(vo.hashStart.intValue() == startV.intValue()){
                    startV = vo.hashEnd + 1;
                    find = true ;
                    break ;
                }
            }
            if(!find){
                throw new Exception("严重错误,未发现哈希值为" + startV + "文件服务器!") ;
            }
            if(startV.intValue() <= 65535){
                doCheck(startV) ;
            }
        }
    }
    public static class Vo{
        public String id ;//id或名称
        public String fileSysAbsolutePath; //文件名称的哈希值对应的文件最终存储绝对路径中的根目录,在配置文件中配置
        public String fileSysRelativePath; //文件名称的哈希值对应的文件最终存储相对路径的目录,在配置文件中配置
        public String restUrl;//文件系统路径
        public String webUrl ;//下载文档的web路径
        public String webDownloadUrl ;//Action下载文档的web路径
        public Integer hashStart ;//哈希值启始值(包含)
        public Integer hashEnd ;//哈希值截止值(包含)
        public Vo(){}
        public Vo(String id,
                String fileSysAbsolutePath,
                String fileSysBasePath,
                String restUrl,
                String webUrl,
                String webDownloadUrl,
                Integer hashStart,
                Integer hashEnd)throws Exception{
            this.id = id ;
            this.fileSysAbsolutePath = fileSysAbsolutePath ;
            this.fileSysRelativePath = fileSysBasePath ;
            this.restUrl = restUrl ;
            this.webUrl = webUrl ;
            this.webDownloadUrl = webDownloadUrl ;
            this.hashStart = hashStart ;
            this.hashEnd = hashEnd ;
            if(this.id == null || this.id.trim().equals("")){
                throw new Exception("出错,id为空了!") ;
            }else{
                this.id = this.id.trim() ;
            }
            if(this.fileSysAbsolutePath == null || this.fileSysAbsolutePath.trim().equals("")){
                throw new Exception("出错,fileSysAbsolutePath为空了!") ;
            }else{
                this.fileSysAbsolutePath = this.fileSysAbsolutePath.trim() ;
                this.fileSysAbsolutePath = this.fileSysAbsolutePath.replaceAll("\\\\", "/") ;
                if(!this.fileSysAbsolutePath.endsWith("/")){
                    this.fileSysAbsolutePath = this.fileSysAbsolutePath + "/" ;
                }
            }
            if(this.fileSysRelativePath == null || this.fileSysRelativePath.trim().equals("")){
                throw new Exception("出错,fileSysBasePath为空了!") ;
            }else{
                this.fileSysRelativePath = this.fileSysRelativePath.trim() ;
            }
            if(this.webUrl == null || this.webUrl.trim().equals("")){
                throw new Exception("出错,webUrl为空了!") ;
            }else{
                this.webUrl = this.webUrl.trim() ;
                if(!this.webUrl.endsWith("/") && !this.webUrl.endsWith("\\")){
                    this.webUrl += "/" ;
                }
            }
            if(this.webDownloadUrl == null || this.webDownloadUrl.trim().equals("")){
                throw new Exception("出错,webDownloadUrl为空了!") ;
            }else{
                this.webDownloadUrl = this.webDownloadUrl.trim() ;
                if(!this.webDownloadUrl.endsWith("/") && !this.webDownloadUrl.endsWith("\\")){
                    this.webDownloadUrl += "/" ;
                }
            }
            if(this.hashStart == null){
                throw new Exception("出错,hashStart为空了!") ;
            }else if(this.hashStart.intValue() < 0){
                throw new Exception("出错,hashStart小于0了!") ;
            }else if(this.hashStart.intValue() > 65535){
                throw new Exception("出错,hashStart大于65535了!") ;
            }
            if(this.hashEnd == null){
                throw new Exception("出错,hashEnd为空了!") ;
            }else if(this.hashEnd.intValue() < 0){
                throw new Exception("出错,hashEnd小于0了!") ;
            }else if(this.hashEnd.intValue() > 65535){
                throw new Exception("出错,hashEnd大于65535了!") ;
            }
            if(this.hashEnd < this.hashStart){
                throw new Exception("出错,hashEnd小于hashStart了!") ;
            }
        }
        public String toString(){
            return "id=" + id + "\n"
                    + "fileSysAbsolutePath=" + fileSysAbsolutePath + "\n"
                    + "fileSysBasePath=" + fileSysRelativePath + "\n"
                    + "restUrl=" + restUrl + "\n"
                    + "webUrl=" + webUrl + "\n"
                    + "webDownloadUrl=" + webDownloadUrl + "\n"
                    + "hashStart=" + hashStart + "\n"
                    + "hashEnd=" + hashEnd ;
        }
        private boolean checkEqual(Vo vo)throws Exception{
            if(this.id.equalsIgnoreCase(vo.id)){
                throw new Exception("出错,id有重复!") ;
            }
            if(this.hashStart.intValue() == vo.hashStart.intValue()){
                throw new Exception("出错,hashStart有重复!") ;
            }
            if(this.hashEnd.intValue() == vo.hashEnd.intValue()){
                throw new Exception("出错,hashEnd有重复!") ;
            }
            if(this.hashStart.intValue() == vo.hashEnd.intValue()){
                throw new Exception("出错,hashStart与hashEnd有重复!") ;
            }
            if(this.hashEnd.intValue() == vo.hashStart.intValue()){
                throw new Exception("出错,hashEnd与hashStart有重复!") ;
            }
            return true ;
        }
    }
}
pms-parent/pms-web-other/src/main/java/com/dy/pmsOther/dyFm/DyFmListener.java
New file
@@ -0,0 +1,67 @@
package com.dy.pmsOther.dyFm;
import com.dy.common.util.NumUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.Environment;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
@Component
public class DyFmListener implements ApplicationListener<ApplicationReadyEvent> {
    @Autowired
    private Environment env;
    public static DyFileSvConf.Group dyFileGroup = new DyFileSvConf.Group() ;
    /**
     * SpringBoot容器已经准备好了
     * @param event 事件
     */
    @Override
    public void onApplicationEvent(@NonNull ApplicationReadyEvent event) {
        try {
            //等1秒,等待com.alibaba.druid.pool.DruidDataSource实始化完成
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            try{
                parseConfig() ;
            }catch(Exception e){
                e.printStackTrace();
                dyFileGroup = null ;
            }
        }
    }
    private void parseConfig() throws Exception{
        String fmUrl = env.getProperty("dy.webFile.fmUrl");
        for(int i = 1 ; i <= 12 ; i++){
            String id = env.getProperty("dy.webFile.sv" + i + ".id");
            String absolutePath = env.getProperty("dy.webFile.sv" + i + ".absolutePath");
            String relativePath = env.getProperty("dy.webFile.sv" + i + ".relativePath");
            String hashStart = env.getProperty("dy.webFile.sv" + i + ".hashStart");
            String hashEnd = env.getProperty("dy.webFile.sv" + i + ".hashEnd");
            String restUrl = env.getProperty("dy.webFile.sv" + i + ".restUrl");
            String webUrl = env.getProperty("dy.webFile.sv" + i + ".webUrl");
            String webDownloadUrl = env.getProperty("dy.webFile.sv" + i + ".webDownloadUrl");
            if(!NumUtil.isPlusIntNumber(hashStart)){
                throw new Exception("配置dy.webFile.sv" + i + ".hashStart 不是整数") ;
            }
            if(!NumUtil.isPlusIntNumber(hashEnd)){
                throw new Exception("配置dy.webFile.sv" + i + ".hashEnd 不是整数") ;
            }
            dyFileGroup.add(new DyFileSvConf.Vo(id,
                    absolutePath,
                    relativePath,
                    restUrl,
                    webUrl,
                    webDownloadUrl,
                    Integer.parseInt(hashStart),
                    Integer.parseInt(hashEnd))) ;
        }
        dyFileGroup.check();
    }
}
pms-parent/pms-web-other/src/main/java/com/dy/pmsOther/dyFm/FileName.java
New file
@@ -0,0 +1,20 @@
package com.dy.pmsOther.dyFm;
import com.dy.common.util.DateTime;
public class FileName {
    public static String createFileName(boolean useYmdhms, String id, String fileExtName){
        if(useYmdhms){
            return DateTime.yyyyMMddHHmmss() + "_" + id + "." + fileExtName ;
        }else{
            return id + "." + fileExtName ;
        }
    }
    public static String createIconName(String clientId, String fileExtName){
        return clientId + "_" + DateTime.yyyyMMddHHmmss() + "." + fileExtName ;
    }
}
pms-parent/pms-web-other/src/main/java/com/dy/pmsOther/dyFm/FileNameIdUtil.java
New file
@@ -0,0 +1,112 @@
package com.dy.pmsOther.dyFm;
import java.util.Calendar;
public class FileNameIdUtil {
    private static int add = 0 ;
    private static int chengShu = 1000 ;
    private static int maxAdd = 999 ;
    private static long last = 0 ;
    //后缀
    //在分布式系统中,例如多个业务中间件dataMw,多个系统都会向数据库中插入数据,用的都是此ID生成器,
    //此ID生成器在各个子系统中难免为同一类数据生成相同的ID,造成数据库插入因主键相同而报错,
    //所以设计此后缀,每个子系统后缀不同
    private static String suffix = "0" ;
    static {
        last = current() ;
    }
    /**
     * 为自实现程序提供的ID生成器
     * 15长度ID,年度取两位,如果是17位长度ID,年度取四位,那17位数字超出了javascript的表数范围
     */
    public  String generate(){
        return doGenerate() ;
    }
    /**
     * 设置后缀,不同子系统设置不同的后缀
     * @param suffix
     */
    public static void setSuffix(String suffix)throws Exception{
        if(suffix == null || suffix.trim().equals("")){
            throw new Exception("后缀不能为空") ;
        }
        FileNameIdUtil.suffix = suffix.trim() ;
    }
    /**
     * 执行生成
     * @return
     */
    private synchronized String doGenerate(){
        Long id = null ;
        long now = current() ;
        if(now != last){
            //上次生成ID 与本次生成ID 不在同一秒内
            last = now ;
            add = 0 ;
            id = last * chengShu + add ++;
        }else{
            //上次生成ID 与本次生成ID 在同一秒内
            if(add == maxAdd){
                //附加量已经用尽
                waitNextSecond(last) ;//等到下一秒
                id = last * chengShu + add ++ ;//返回上一秒生成的ID
                add = 0 ;//附加量归零,为下一秒准备
            }else{
                //附加量未用尽
                id = last * chengShu + add ++ ;
            }
        }
        return id + suffix ;
    }
    /**
     * 等待下一秒到来
     * @param last
     */
    private void waitNextSecond(Long last){
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
        }finally{
            long now = current() ;
            if(now == last){
                waitNextSecond(last) ;
            }
        }
    }
    /**
     * 格式为 150516010203
     * @return
     */
    private static long current(){
        Calendar cal = Calendar.getInstance();
        long d = (cal.get(Calendar.YEAR) % 100) * 10000000000L
        + (cal.get(Calendar.MONTH) + 1) * 100000000L
        + cal.get(Calendar.DAY_OF_MONTH) * 1000000L
        + cal.get(Calendar.HOUR_OF_DAY) * 10000L
        + cal.get(Calendar.MINUTE) * 100L
        + cal.get(Calendar.SECOND)  ;
        return d ;
    }
    public static void main(String args[]){
        FileNameIdUtil o = new FileNameIdUtil() ;
        int total = 800 ;
        long start = System.currentTimeMillis() ;
        for(int i = 0 ; i < total ; i++){
            System.out.println((String)(o.generate())) ;
        }
        long end = System.currentTimeMillis() ;
        System.out.println("产生" + total + "ID用时" + (end - start) + "毫秒");
    }
}
pms-parent/pms-web-other/src/main/java/com/dy/pmsOther/dyFm/FileRestVo.java
New file
@@ -0,0 +1,28 @@
package com.dy.pmsOther.dyFm;
import lombok.Data;
@Data
public class FileRestVo {
    public String fileName ; //生成的文件名称,例如20170818153254_100000007.jpg
    public Integer fileNameHash ; //文件名称的哈希值
    public String fileSysId; //文件名称的哈希值对应的文件系统的id,在配置文件中配置
    public String fileSysAbsolutePath; //文件名称的哈希值对应的文件最终存储绝对路径中的根目录,在配置文件中配置
    public String fileSysRelativePath; //文件名称的哈希值对应的文件最终存储相对路径的目录,在配置文件中配置
    public String fileSysRestUrl; //文件名称的哈希值对应的文件系统的restful URL,在配置文件中配置
    public String fileWebUrl; //文件名称的哈希值对应的文件系统的下载文件的web URL,在配置文件中配置
    public String fileWebDownloadUrl; //文件名称的哈希值对应的文件系统的Action下载文件的web URL,在配置文件中配置
    public String toString(){
        return "fileName=" + fileName + "\n"
                + "fileNameHash=" + fileNameHash + "\n"
                + "sysId=" + fileSysId + "\n"
                + "fileSysAbsolutePath=" + fileSysAbsolutePath + "\n"
                + "fileSysRelativePath=" + fileSysRelativePath + "\n"
                + "restUrl=" + fileSysRestUrl + "\n"
                + "fileWebUrl=" + fileWebUrl + "\n"
                + "fileWebDownloadUrl=" + fileWebDownloadUrl ;
    }
}
pms-parent/pms-web-other/src/main/java/com/dy/pmsOther/dyFm/FmCtrl.java
New file
@@ -0,0 +1,133 @@
package com.dy.pmsOther.dyFm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
 * 单点登录,
 */
@Slf4j
@RestController
@RequestMapping(path="fm")
@SuppressWarnings("unchecked")//java版本越高,对泛型约束越严,所以配置SuppressWarnings("unchecked")
public class FmCtrl {
    /**
     * 生成文件名,并且计算出该文件名对应的文件服务器属性
     * 文件名称是不带路径的名称,例如:20170818153254_100000007.jpg,文件名中不能有“/”或“\”字符
     * @param fileExtName 文件扩展名
     * @return
     */
    @PostMapping(path = "create")
    public FileRestVo create(String fileExtName){
        FileRestVo rvo = new FileRestVo() ;
        if(fileExtName != null && !fileExtName.trim().equals("")){
            try {
                String id = new FileNameIdUtil().generate();
                if(id != null){
                    rvo.fileName = FileName.createFileName(false, id, fileExtName) ;
                    rvo = new RestHashDeal().fileTransRest(rvo.fileName, rvo) ;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return rvo ;
    }
    /**
     * 解析文件名(带或不带相对路径),计算出该文件名对应的文件服务器属性
     * 文件名称可以是不带路径的名称,例如:20170818153254_100000007.jpg
     * 文件名称可以是带路径的名称,例如:webFile/photo/20170818153254_100000007.jpg
     * @param filePath
     * @return
     */
    @PostMapping(path = "parsePath")
    public FileRestVo parsePath(String filePath){
        FileRestVo rvo = new FileRestVo() ;
        if(filePath != null && !filePath.trim().equals("")){
            try {
                int index = filePath.lastIndexOf("\\") ;
                if(index > 0){
                    filePath = filePath.substring(index + 1);
                }
                index = filePath.lastIndexOf("/") ;
                if(index > 0){
                    filePath = filePath.substring(index + 1);
                }
                index = filePath.lastIndexOf("?") ;
                if(index > 0){
                    filePath = filePath.substring(0, index);
                }
                rvo.fileName = filePath ;
                rvo = new RestHashDeal().fileTransRest(filePath, rvo) ;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return rvo ;
    }
    /**
     * 解析文件名,并且计算出该文件名对应的文件服务器属性
     * 文件名称可以是不带路径的名称,例如:20170818153254_100000007.jpg
     * 文件名称可以是带路径的名称,例如:webFile/photo/20170818153254_100000007.jpg
     * @param filePaths
     * @return
     */
    @PostMapping(path = "parsePathList", consumes = MediaType.APPLICATION_JSON_VALUE)//前端提交json数据
    public FileRestVo[] parsePathList(List<String> filePaths){
        List<FileRestVo> rList = new ArrayList<FileRestVo>() ;
        if(filePaths != null && filePaths.size() > 0){
            try {
                for(String filePath : filePaths){
                    FileRestVo rvo = new FileRestVo() ;
                    int index = filePath.lastIndexOf("\\") ;
                    if(index > 0){
                        filePath = filePath.substring(index + 1);
                    }
                    index = filePath.lastIndexOf("/") ;
                    if(index > 0){
                        filePath = filePath.substring(index + 1);
                    }
                    index = filePath.lastIndexOf("?") ;
                    if(index > 0){
                        filePath = filePath.substring(0, index);
                    }
                    rvo.fileName = filePath ;
                    rvo = new RestHashDeal().fileTransRest(filePath, rvo) ;
                    rList.add(rvo) ;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return rList.toArray(new FileRestVo[0]) ;
    }
    /**
     * 解析哈希值,计算出该哈希值对应的文件服务器属性
     * @param hashCode
     * @return
     */
    @PostMapping(path = "parseHashcode")
    public FileRestVo parseHashcode(Integer hashCode){
        FileRestVo rvo = new FileRestVo() ;
        if(hashCode != null){
            try {
                rvo.fileName = null ;
                rvo = new RestHashDeal().fileTransRest(hashCode, rvo) ;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return rvo ;
    }
}
pms-parent/pms-web-other/src/main/java/com/dy/pmsOther/dyFm/RestHashDeal.java
New file
@@ -0,0 +1,95 @@
package com.dy.pmsOther.dyFm;
import com.dy.common.util.MurmurHash ;
public class RestHashDeal {
    /**
     * 文件名称计算转换
     * @param fileName
     * @param rvo
     * @return
     * @throws Exception
     */
    public FileRestVo fileTransRest(String fileName, FileRestVo rvo) throws Exception{
        if(fileName != null
                && !fileName.trim().equals("")
                && rvo != null){
            if(DyFmListener.dyFileGroup == null
                    || DyFmListener.dyFileGroup.list == null
                    || DyFmListener.dyFileGroup.list.size() == 0){
                throw new Exception("严重错误,文件服务器restful未配置!") ;
            }else{
                DyFileSvConf.Vo confVo = null ;
                Integer hash = null ;
                if(DyFmListener.dyFileGroup.list.size() == 1){
                    confVo = DyFmListener.dyFileGroup.list.get(0) ;
                }else{
                    hash = new MurmurHash().hash16_plus(fileName) ;
                    for(DyFileSvConf.Vo lvo : DyFmListener.dyFileGroup.list){
                        if(hash >= lvo.hashStart.intValue()
                                && hash <= lvo.hashEnd.intValue()){
                            confVo = lvo ;
                            break ;
                        }
                    }
                }
                if(confVo != null){
                    rvo.fileSysId = confVo.id;
                    rvo.fileNameHash = hash;
                    rvo.fileSysAbsolutePath = confVo.fileSysAbsolutePath;
                    rvo.fileSysRelativePath = confVo.fileSysRelativePath;
                    rvo.fileSysRestUrl = confVo.restUrl;
                    rvo.fileWebUrl = confVo.webUrl ;
                    rvo.fileWebDownloadUrl = confVo.webDownloadUrl ;
                }
            }
        }
        return rvo ;
    }
    /**
     * 哈希值计算转换
     * @param hashcode
     * @param rvo
     * @return
     * @throws Exception
     */
    public FileRestVo fileTransRest(Integer hashcode, FileRestVo rvo) throws Exception{
        if(hashcode != null
                && rvo != null){
            if(DyFmListener.dyFileGroup == null
                    || DyFmListener.dyFileGroup.list == null
                    || DyFmListener.dyFileGroup.list.size() == 0){
                throw new Exception("严重错误,文件服务器restful未配置!") ;
            }else{
                DyFileSvConf.Vo confVo = null ;
                Integer hash = hashcode ;
                if(DyFmListener.dyFileGroup.list.size() == 1){
                    confVo = DyFmListener.dyFileGroup.list.get(0) ;
                }else{
                    for(DyFileSvConf.Vo lvo : DyFmListener.dyFileGroup.list){
                        if(hash >= lvo.hashStart.intValue()
                                && hash <= lvo.hashEnd.intValue()){
                            confVo = lvo ;
                            break ;
                        }
                    }
                }
                if(confVo != null){
                    rvo.fileSysId = confVo.id;
                    rvo.fileNameHash = hash;
                    rvo.fileSysAbsolutePath = confVo.fileSysAbsolutePath;
                    rvo.fileSysRelativePath = confVo.fileSysRelativePath;
                    rvo.fileSysRestUrl = confVo.restUrl;
                    rvo.fileWebUrl = confVo.webUrl ;
                    rvo.fileWebDownloadUrl = confVo.webDownloadUrl ;
                }
            }
        }
        return rvo ;
    }
}
pms-parent/pom.xml
@@ -48,6 +48,8 @@
        <!-- sso用到的缓存框架 -->
        <spring-boot-starter-cache.version>3.2.4</spring-boot-starter-cache.version>
        <caffeine.version>3.1.8</caffeine.version>
        <!-- 生成图片缩略图 -->
        <thumbnailator.version>0.4.20</thumbnailator.version>
        <spring-boot-maven-plugin.version>3.2.0</spring-boot-maven-plugin.version>
        <maven-jar-plugin-plugin.version>3.3.0</maven-jar-plugin-plugin.version>