package com.artfess.file.persistence.manager.impl;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.artfess.base.attachment.AttachmentService;
import com.artfess.base.attachment.AttachmentServiceFactory;
import com.artfess.base.constants.CodePrefix;
import com.artfess.base.context.BaseContext;
import com.artfess.base.enums.FileTypeEnum;
import com.artfess.base.exception.ApplicationException;
import com.artfess.base.exception.BaseException;
import com.artfess.base.exception.RequiredException;
import com.artfess.base.feign.UCFeignService;
import com.artfess.base.handler.MultiTenantHandler;
import com.artfess.base.handler.MultiTenantIgnoreResult;
import com.artfess.base.manager.impl.BaseManagerImpl;
import com.artfess.base.model.CommonResult;
import com.artfess.base.util.*;
import com.artfess.base.util.time.TimeUtil;
import com.artfess.file.attachmentService.FtpAttachmentServiceImpl;
import com.artfess.file.config.UploadResult;
import com.artfess.file.model.DefaultFile;
import com.artfess.file.params.FileStorageConfigDTO;
import com.artfess.file.params.FlowUploadPropertiesStorageDTO;
import com.artfess.file.persistence.dao.FileDao;
import com.artfess.file.persistence.manager.FileConfigManager;
import com.artfess.file.persistence.manager.FileManager;
import com.artfess.file.persistence.manager.FlowUploadPropertiesManager;
import com.artfess.file.service.FilePreview;
import com.artfess.file.service.FilePreviewFactory;
import com.artfess.file.util.AppFileUtil;
import com.artfess.file.util.FileUtils;
import com.artfess.file.util.MinioUtil;
import com.artfess.file.util.UploadM3u8;
import com.artfess.file.vo.FileReqVo;
import com.artfess.file.vo.MultipartCompleteParam;
import com.artfess.file.vo.MultipartInitParam;
import com.artfess.file.vo.MultipartInitRespVo;
import com.artfess.redis.util.RedisUtil;
import com.artfess.sysConfig.util.SysPropertyUtil;
import com.artfess.uc.api.model.IUser;
import com.artfess.uc.api.service.IUserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

@Slf4j
@Service("fileManager")
public class FileManagerImpl extends BaseManagerImpl<FileDao, DefaultFile> implements FileManager {
    @Resource
    UCFeignService ucFeignService;
    @Resource
    IUserService userService;
    @Resource
    BaseContext baseContext;
    @Resource
    FileConfigManager fileConfigManager;
    @Resource
    private FileDao fileDao;

    private static final String CHUNK_UPLOAD_PREFIX = "/file/v1/chunkUpload";
    @Resource
    FilePreviewFactory previewFactory;
    @Autowired
    private RedisUtil redisUtil;
    @Resource(name = "bmpExecutorService")
    private ExecutorService executorService;

    // 初始化当前附件处理器

    private AttachmentService attachmentService;

    private void initCurrentHandler(String propertiesId) throws Exception {
        AttachmentServiceFactory attachmentHandlerFactory = AppUtil.getBean(AttachmentServiceFactory.class);
        attachmentService = attachmentHandlerFactory.getCurrentServices(AppFileUtil.getSaveType(propertiesId));
    }

    // 初始化当前附件处理器
    private void initCurrentHandlerByBizCode(String bizCode) throws Exception {
        AttachmentServiceFactory attachmentHandlerFactory = AppUtil.getBean(AttachmentServiceFactory.class);
        attachmentService = attachmentHandlerFactory.getCurrentServices(AppFileUtil.getFileSaveTypeByConfig(bizCode));
    }

    @Override
    public List<DefaultFile> getAllByExt(String[] allowFiles) {
        return baseMapper.getAllByExt(allowFiles);
    }

    @Override
    public void updateFileBizIdByIds(List<String> idList, String bizId) {
        // 先通过bizid将以前的数据状态修改为删除
        this.removeFileByBizId(bizId,idList);

        // 然后将现在的修改为未删除并关联数据ID
//        UpdateWrapper<DefaultFile> defaultFileUpdateWrapper = new UpdateWrapper<>();
//        defaultFileUpdateWrapper.set("BIZ_ID_", bizId).set("IS_DEL_", "0").in("id_", idList);
//        this.update(defaultFileUpdateWrapper);
        fileDao.updateFileDelAndBizIdByIds("0", bizId, null, idList);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateFilesByIds(List<DefaultFile> files, String bizId) {
        if (StringUtil.isEmpty(bizId)) {
            return;
        }

        // 新文件为空 -> 删除全部
        if (CollectionUtils.isEmpty(files)) {
            LambdaQueryWrapper<DefaultFile> deleteWrapper = new LambdaQueryWrapper<>();
            deleteWrapper.eq(DefaultFile::getBizId, bizId);
            this.remove(deleteWrapper);
            return;
        }

        // 新文件不为空，先把新文件ID收集
        List<String> newFileIds = files.stream()
                .map(DefaultFile::getId)
                .collect(Collectors.toList());

        // 查询数据库时，条件：bizId相同 或 ID在新文件列表中
        LambdaQueryWrapper<DefaultFile> fileWrapper = new LambdaQueryWrapper<>();
        fileWrapper.eq(DefaultFile::getBizId, bizId)
                .or()
                .in(DefaultFile::getId, newFileIds);
        List<DefaultFile> dbFiles = this.list(fileWrapper);

        // 新文件按ID映射
        Map<String, DefaultFile> newFileMap = files.stream()
                .collect(Collectors.toMap(DefaultFile::getId, f -> f));

        // 循环查出来的数据，与新文件匹配 匹配失败的就删除，成功只修改bizId及bizCode
        for (DefaultFile dbFile : dbFiles) {
            if(newFileMap.containsKey(dbFile.getId())){
                DefaultFile newFile = newFileMap.get(dbFile.getId());
                dbFile.setBizId(bizId);
                dbFile.setBizCode(newFile.getBizCode());
            }else {
                dbFile.setIsDel(Short.valueOf("1"));
            }
        }
        // 批量保存/更新
        this.saveOrUpdateBatch(dbFiles);
    }

    @Override
    @Transactional
    public void updateFilesByIds(String fileId, String bizId, String bizCode) {
        LambdaQueryWrapper<DefaultFile> fileWrapper = new LambdaQueryWrapper<>();
        if (fileId == null) {
            fileWrapper.eq(DefaultFile::getBizId, bizId);
            this.remove(fileWrapper);
            return;
        }
        if(StringUtil.isEmpty(bizCode) || StringUtil.isEmpty(bizId)) {
            throw new BaseException(" 业务id、业务分组Code 不能为空！");
        }
        fileWrapper.eq(DefaultFile::getBizId, bizId);
        fileWrapper.eq(DefaultFile::getBizCode, bizCode);
        fileWrapper.eq(DefaultFile::getIsDel, "0");
        this.remove(fileWrapper);
        //更新附件
        fileDao.updateFileDelAndBizIdById("0", bizId, bizCode, fileId);
    }

    @Override
    public void removeFileByBizId(String bizId,List<String> idList) {
        // 先通过bizid将以前的数据状态修改为删除
        UpdateWrapper<DefaultFile> deleOldFileUpdateWrapper = new UpdateWrapper<>();
        deleOldFileUpdateWrapper.set("IS_DEL_", "1").eq("BIZ_ID_", bizId).notIn("ID_",idList);
        this.update(deleOldFileUpdateWrapper);
    }

    @Override
    public String minioUrl(String fileId) throws Exception {
        DefaultFile fileMode = this.get(fileId);
        Assert.notNull(fileMode, "要查看的文件不存在");
        String saveType = fileMode.getStoreType();
        if (!DefaultFile.SAVE_TYPE_MINIO.equals(saveType)) {
            throw new BaseException("你要获取的文件不是使用minio进行保存的");
        }
        AttachmentServiceFactory attachmentHandlerFactory = AppUtil.getBean(AttachmentServiceFactory.class);
        attachmentService = attachmentHandlerFactory.getCurrentServices(saveType);
        return attachmentService.getUrl(fileMode);
    }

    @Override
    public List<DefaultFile> queryFileByBizId(String bizId) {
        QueryWrapper<DefaultFile> defaultFileQueryWrapper = new QueryWrapper<>();
        defaultFileQueryWrapper.eq("BIZ_ID_", bizId).eq("is_del_", 0);
        return this.list(defaultFileQueryWrapper);
    }

    @Override
    public List<DefaultFile> queryFileByBizId(String bizId, String bizCode) {
        QueryWrapper<DefaultFile> defaultFileQueryWrapper = new QueryWrapper<>();
        defaultFileQueryWrapper.eq("BIZ_ID_", bizId).eq("is_del_", 0).eq("BIZ_CODE_", bizCode);
        return this.list(defaultFileQueryWrapper);
    }


//    public void removeFileByBizId(String bizId,List<String> idList) {
//        // 先通过bizid将以前的数据状态修改为删除
//        UpdateWrapper<DefaultFile> deleOldFileUpdateWrapper = new UpdateWrapper<>();
//        deleOldFileUpdateWrapper.set("IS_DEL_", "1").eq("BIZ_ID_", bizId).notIn("ID_",idList);
//        this.update(deleOldFileUpdateWrapper);
//    }

    @Override
    @Transactional
    public void delSysFileByIds(String[] ids) throws Exception {
        String propertiesId = "";
        if (BeanUtils.isNotEmpty(ids) && ids.length > 0) {
            DefaultFile file = this.get(ids[0]);
            if (BeanUtils.isNotEmpty(file)) {
                propertiesId = file.getProp6();
            }
        }
        initCurrentHandler(propertiesId);
        for (String id : ids) {
            DefaultFile def = this.get(id);
            attachmentService.remove(def, BeanUtils.isNotEmpty(def) ? def.getProp6() : "");
            baseMapper.deleteById(id);
        }
    }

    @Override
    public UploadResult UploadFileForConfig(DefaultFile file, List<MultipartFile> files, String bizCode, String bizId, String bizType, IUser currentUser) throws Exception {
        Iterator<MultipartFile> it = files.iterator();
        initCurrentHandlerByBizCode(bizCode);
        FileStorageConfigDTO fileStorageConfig = fileConfigManager.getFileConfigByCode(bizCode);
        String fileFormates = fileStorageConfig.getFileKinds();
        Double allowSize = fileStorageConfig.getAllowSize();
        int allowMultiple = fileStorageConfig.getAllowMultiple();
        UploadResult result = new UploadResult();
        if (allowMultiple == 0 && files.size() > 1) {
            result.setSuccess(false);
            result.setMessage("系统只能上传一个附件！");
            return result;
        }

        short isdel = 0;
        while (it.hasNext()) {
            Boolean isAdd = false;
            String oldFilePath = "";
            DefaultFile sysFile;
            if (StringUtil.isNotEmpty(file.getId())) {
                sysFile = this.get(file.getId());
                oldFilePath = sysFile.getFilePath();
            } else {
                // 新增的上传文件
                isAdd = true;
                sysFile = new DefaultFile();
                // 生成新的文件ID
                sysFile.setId(UniqueIdUtil.getSuid());
            }
            MultipartFile f = it.next();
            if (StringUtil.isNotEmpty(file.getFileName())) {
                if (!file.getFileName().equals(f.getName())) {
                    throw new RuntimeException("上传名称与文件名称不符");
                }
            }
            String oriFileName = f.getOriginalFilename();
            String extName = FileUtil.getFileExt(oriFileName);

            //文件格式要求
            if (StringUtil.isNotEmpty(fileFormates)) {
                //不符合文件格式要求的就标志为false
                List<String> typeList = Arrays.asList(fileFormates.split(","));
                for (int i = 0; i < typeList.size(); i++) {
                    if (StringUtil.isNotEmpty(typeList.get(i))) {
                        typeList.set(i, typeList.get(i).toLowerCase());
                    }
                }
                if (typeList.indexOf(extName) < 0 && typeList.indexOf("." + extName) < 0) {
                    result.setSuccess(false);
                    result.setMessage("系统不允许该类型文件的上传！:" + extName);
                    return result;
                }
            }

            if (allowSize != null && allowSize > 0d) {
                long len = f.getSize();
                double fileSize = (double) len / 1048576;
                if (fileSize > allowSize) {
                    result.setSuccess(false);
                    result.setMessage("上传文件【" + oriFileName + "】大小已超过系统限制：" + fileSize + "MB！");
                    return result;
                }
            }

            String fileName = sysFile.getId() + "." + extName;
            String filePath = "";

            String creatorAccount = DefaultFile.FILE_UPLOAD_UNKNOWN;
            // 当前用户的信息
            if (currentUser != null) {
                sysFile.setCreateBy(currentUser.getUserId());
                sysFile.setCreatorName(currentUser.getFullname());
                creatorAccount = currentUser.getAccount();
            } else {
                sysFile.setCreatorName(creatorAccount);
            }

            if ("pictureShow".equals(sysFile.getFileType())) {
                filePath = AppFileUtil.createFilePath(creatorAccount + File.separator + "pictureShow", fileName);
            } else {
                filePath = AppFileUtil.createFilePath(currentUser.getAccount(), fileName);
            }
            // 附件名称
            sysFile.setFileName(oriFileName.lastIndexOf('.') == -1 ? oriFileName : oriFileName.substring(0, oriFileName.lastIndexOf('.')));
            sysFile.setFilePath(filePath);
            sysFile.setBizCode(bizCode);
            sysFile.setBizId(bizId);
            sysFile.setBizType(bizType);
            sysFile.setIsZone(0);
            sysFile.setZoneTotal(1L);
            sysFile.setMd5Value(DigestUtils.md5DigestAsHex(f.getInputStream()));
            sysFile.setServerLocalName(fileName);
            String rootPath = SysPropertyUtil.getByAlias("file.upload");
            sysFile.setServerLocalPath(rootPath + filePath);
            // 上传时间
            sysFile.setCreateTime(LocalDateTime.now());
            // 扩展名
            sysFile.setExtensionName(extName);
            // 字节总数
            sysFile.setByteCount(f.getSize());
            // 说明

            sysFile.setIsDel(isdel);
            sysFile.setNote(FileUtil.getSize(f.getSize()));
            sysFile.setProp6(fileStorageConfig.getStorageId());

            sysFile.setStoreType(fileStorageConfig.getUploadType());
            AttachmentServiceFactory attachmentHandlerFactory = AppUtil.getBean(AttachmentServiceFactory.class);
            attachmentService = attachmentHandlerFactory.getCurrentServices(sysFile.getStoreType());
            // 总的字节数
            if (isAdd) {
                sysFile.setBytes(f.getBytes());
                attachmentService.upload(sysFile, f.getInputStream(), BeanUtils.isNotEmpty(sysFile) ? sysFile.getProp6() : "");
                if (!fileStorageConfig.getUploadType().equals(DefaultFile.SAVE_TYPE_DTABASE)) {
                    sysFile.setBytes(null);
                }
                super.create(sysFile);
            } else {
                attachmentService.upload(sysFile, f.getInputStream(), BeanUtils.isNotEmpty(sysFile) ? sysFile.getProp6() : "");
                this.update(sysFile);
                boolean tag = true;
                String newFilePath = sysFile.getFilePath();
                if (StringUtil.isNotEmpty(newFilePath) && StringUtil.isNotEmpty(oldFilePath)) {
                    if (newFilePath.trim().equals(oldFilePath.trim())) {
                        tag = false;
                    }
                }
                // 修改了文件的存放路径，需要删除之前路径下的文件
                if (tag) {
                    sysFile.setFilePath(oldFilePath);
                    attachmentService.remove(sysFile, BeanUtils.isNotEmpty(sysFile) ? sysFile.getProp6() : "");
                }
            }
            result.setSuccess(true);
            result.setFileId(sysFile.getId());
            result.setFileName(oriFileName);
            result.setSize(sysFile.getByteCount());
            result.setUsername(sysFile.getCreatorName());
        }
        return result;
    }

    @Override
    @Transactional
    public UploadResult uploadFile(DefaultFile file, List<MultipartFile> files, String fileFormates, IUser currentUser, String flowKey) throws Exception {
        Iterator<MultipartFile> it = files.iterator();
        initCurrentHandler(flowKey);
        UploadResult result = new UploadResult();
        boolean mark = true;
        short isdel = 0;
        while (it.hasNext()) {
            Boolean isAdd = false;
            String oldFilePath = "";
            DefaultFile sysFile;
            if (StringUtil.isNotEmpty(file.getId())) {
                sysFile = this.get(file.getId());
                oldFilePath = sysFile.getFilePath();
            } else {
                // 新增的上传文件
                isAdd = true;
                sysFile = new DefaultFile();
                // 生成新的文件ID
                sysFile.setId(UniqueIdUtil.getSuid());
            }
            MultipartFile f = it.next();
            if (StringUtil.isNotEmpty(file.getFileName())) {
                if (!file.getFileName().equals(f.getName())) {
                    throw new RuntimeException("上传名称与文件名称不符");
                }
            }
            String oriFileName = f.getOriginalFilename();
            String extName = FileUtil.getFileExt(oriFileName);
            //文件格式要求
            if (StringUtil.isNotEmpty(fileFormates)) {
                //不符合文件格式要求的就标志为false
                List<String> typeList = Arrays.asList(fileFormates.split(","));
                for (int i = 0; i < typeList.size(); i++) {
                    if (StringUtil.isNotEmpty(typeList.get(i))) {
                        typeList.set(i, typeList.get(i).toLowerCase());
                    }
                }
                if (typeList.indexOf(extName) < 0 && typeList.indexOf("." + extName) < 0) {
                    mark = false;
                }
            }
            if (mark) {
                String fileName = sysFile.getId() + "." + extName;
                String filePath = "";

                String creatorAccount = DefaultFile.FILE_UPLOAD_UNKNOWN;
                // 当前用户的信息
                if (currentUser != null) {
                    sysFile.setCreateBy(currentUser.getUserId());
                    sysFile.setCreatorName(currentUser.getFullname());
                    creatorAccount = currentUser.getAccount();
                } else {
                    sysFile.setCreatorName(creatorAccount);
                }
                if ("pictureShow".equals(sysFile.getFileType())) {
                    filePath = AppFileUtil.createFilePath(creatorAccount + File.separator + "pictureShow", fileName);
                } else {
                    filePath = AppFileUtil.createFilePath(currentUser.getAccount(), fileName);
                }
                // 附件名称
                sysFile.setFileName(oriFileName.lastIndexOf('.') == -1 ? oriFileName : oriFileName.substring(0, oriFileName.lastIndexOf('.')));
                sysFile.setFilePath(filePath);
                // 上传时间
                sysFile.setCreateTime(LocalDateTime.now());
                // 扩展名
                sysFile.setExtensionName(extName);
                // 字节总数
                sysFile.setByteCount(f.getSize());
                // 说明

                sysFile.setIsDel(isdel);
                sysFile.setNote(FileUtil.getSize(f.getSize()));

                //流程key
                if (StringUtil.isNotEmpty(flowKey)) {
                    FlowUploadPropertiesManager uploadPropertiesManager = AppUtil.getBean(FlowUploadPropertiesManager.class);
                    FlowUploadPropertiesStorageDTO uploadProperties = uploadPropertiesManager.getByFlowKey(flowKey);
                    if (BeanUtils.isNotEmpty(uploadProperties)) {
                        sysFile.setProp6(uploadProperties.getId());
                    }
                }
                String saveType = null;
                if (StringUtil.isEmpty(file.getStoreType())) {
                    saveType = AppFileUtil.getSaveType(flowKey);
                    sysFile.setStoreType(saveType);
                } else {
                    saveType = file.getStoreType();
                    sysFile.setStoreType(saveType);
                }
                if (DefaultFile.SAVE_TYPE_MINIO.equals(saveType)) {
                    LocalDate now = LocalDate.now();
                    String showPath=now.getYear()+"-"+now.getMonthValue()+"/"+fileName;
                    sysFile.setFilePath(showPath);
                }
                AttachmentServiceFactory attachmentHandlerFactory = AppUtil.getBean(AttachmentServiceFactory.class);
                attachmentService = attachmentHandlerFactory.getCurrentServices(saveType);
                // 总的字节数
                if (isAdd) {
                    sysFile.setBytes(f.getBytes());
                    attachmentService.upload(sysFile, f.getInputStream(), BeanUtils.isNotEmpty(sysFile) ? sysFile.getProp6() : "");
                    if (!saveType.equals(DefaultFile.SAVE_TYPE_DTABASE)) {
                        sysFile.setBytes(null);
                    }
                    super.create(sysFile);
                } else {
                    attachmentService.upload(sysFile, f.getInputStream(), BeanUtils.isNotEmpty(sysFile) ? sysFile.getProp6() : "");
                    this.update(sysFile);
                    boolean tag = true;
                    String newFilePath = sysFile.getFilePath();
                    if (StringUtil.isNotEmpty(newFilePath) && StringUtil.isNotEmpty(oldFilePath)) {
                        if (newFilePath.trim().equals(oldFilePath.trim())) {
                            tag = false;
                        }
                    }
                    // 修改了文件的存放路径，需要删除之前路径下的文件
                    if (tag) {
                        sysFile.setFilePath(oldFilePath);
                        attachmentService.remove(sysFile, BeanUtils.isNotEmpty(sysFile) ? sysFile.getProp6() : "");
                    }
                }
                result.setSuccess(true);
                result.setFileId(sysFile.getId());
                result.setFileName(oriFileName);
                result.setSize(sysFile.getByteCount());
                result.setUsername(sysFile.getCreatorName());
                String suffixName = FileUtils.getSuffixFromFileName(oriFileName);
                String fileType = FileTypeEnum.getType(suffixName);
                if (FileTypeEnum.video.getType().equals(fileType)) {
                    long videoTime = FileUtils.getVideoTime(f);
                    result.setVideoTime(videoTime);
                } else if (FileTypeEnum.txt.getType().equals(fileType) && ".pdf".equals(suffixName)) {
                    //todo 转pdf 1.第一步文档转pdf 2.调用平台上传接口 ，3.返回fileId
                    result.setPdfFileId(UniqueIdUtil.getSuid());
                }
                result.setFileExt(suffixName);
                result.setFileType(fileType);

            } else {
                result.setSuccess(false);
                result.setMessage("系统不允许该类型文件的上传！:" + extName);
            }
        }
        return result;
    }

    @Override
    public DefaultFile downloadFile(String fileId, OutputStream outStream) throws Exception {
        DefaultFile sysFile = this.get(fileId);
        AttachmentServiceFactory attachmentHandlerFactory = AppUtil.getBean(AttachmentServiceFactory.class);
        initCurrentHandler(BeanUtils.isNotEmpty(sysFile) ? sysFile.getProp6() : "");
        if (BeanUtils.isEmpty(sysFile)) {
            return null;
        }//特殊场景，附件上传方式是ftp，word套打生成的文件是磁盘存储，下载套打文件时特殊处理
        else if (attachmentService instanceof FtpAttachmentServiceImpl && DefaultFile.SAVE_TYPE_FOLDER.equals(sysFile.getStoreType())) {

            attachmentService = attachmentHandlerFactory.getCurrentServices(DefaultFile.SAVE_TYPE_FOLDER);
        } else {
            attachmentService = attachmentHandlerFactory.getCurrentServices(sysFile.getStoreType());
        }
        attachmentService.download(sysFile, outStream, BeanUtils.isNotEmpty(sysFile) ? sysFile.getProp6() : "");
        return sysFile;
    }

    @Override
    @Transactional
    public void uploadFile(DefaultFile file, InputStream inputStream) throws Exception {
        initCurrentHandler(BeanUtils.isNotEmpty(file) ? file.getProp6() : "");
        String storeType = file.getStoreType();
        if (StringUtil.isEmpty(file.getId())) {
            file.setId(UniqueIdUtil.getSuid());
        }
        attachmentService.upload(file, inputStream, BeanUtils.isNotEmpty(file) ? file.getProp6() : "");
        if (StringUtil.isEmpty(storeType)) {
            storeType = AppFileUtil.getSaveType(BeanUtils.isNotEmpty(file) ? file.getProp6() : "");
            file.setStoreType(storeType);
        }
        if (!DefaultFile.SAVE_TYPE_DTABASE.equals(storeType)) {
            file.setBytes(null);
        }
        super.create(file);
    }

    @Override
    @Transactional
    public void setXbTypeId(List<String> fileId, String xbTypeId, String type) throws Exception {
        baseMapper.setXbTypeId(fileId, xbTypeId, type);
    }

    @Override
    @Transactional
    public void updateFileExtraProp(List<DefaultFile> files) {
        if (BeanUtils.isNotEmpty(files)) {
            for (DefaultFile defaultFile : files) {
                baseMapper.updateExtraProps(defaultFile);
            }
        }
    }

    @Override
    public byte[] getFileBytesById(String fileId) throws Exception {
        DefaultFile sysFile = this.get(fileId);
        initCurrentHandler(BeanUtils.isNotEmpty(sysFile) ? sysFile.getProp6() : "");
        if (BeanUtils.isEmpty(sysFile)) {
            return null;
        }
        return attachmentService.getFileBytes(sysFile);
    }

    @Override
    public DefaultFile get(Serializable id) {
        DefaultFile file = null;
        try (MultiTenantIgnoreResult setThreadLocalIgnore = MultiTenantHandler.setThreadLocalIgnore()) {
            file = super.get(id);
        } catch (Exception e) {
            throw new BaseException(e.getMessage(), e);
        }
        return file;
    }

    @Override
    public void downloadFileToPath(DefaultFile file, String path) throws Exception {
        initCurrentHandler(BeanUtils.isNotEmpty(file) ? file.getProp6() : "");
        File dfile = new File(path);
        if (!dfile.exists()) {
            if (BeanUtils.isNotEmpty(dfile.getParentFile()) && !dfile.getParentFile().exists()) {
                dfile.getParentFile().mkdir();
            }
            OutputStream out = new FileOutputStream(dfile);
            attachmentService.download(file, out, BeanUtils.isNotEmpty(file) ? file.getProp6() : "");
            out.flush();
            out.close();
        }
    }

    @Override
    public CommonResult<String> importSignature(MultipartFile file, boolean isCover) {
        String rootRealPath = "";
        CommonResult<String> message = new CommonResult<String>(true, "导入成功！");
        try {
            String unZipDir = TimeUtil.getCurrentTimeMillis() + "_" + baseContext.getCurrentUserId();
            rootRealPath = (FileUtil.getIoTmpdir() + "attachFiles/unZip/" + unZipDir).replace("/", File.separator);
            //建立临时文件夹，存放文件
            File folder = new File(rootRealPath);
            if (!folder.exists()) {
                folder.mkdirs();
            }
            ZipUtil.unZipFile(file, rootRealPath);
            File ignatureFloder = new File(rootRealPath);        //获取其file对象
            File[] fs = ignatureFloder.listFiles();    //遍历path下的文件和目录，放在File数组中
            IUser user = null;
            String account = baseContext.getCurrentUserAccout();
            if (StringUtil.isNotEmpty(account)) {
                user = userService.getUserByAccount(account);
            }
            ArrayNode accountFiles = JsonUtil.getMapper().createArrayNode();
            // 创建一个线程池
            ExecutorService pool = Executors.newFixedThreadPool(fs.length);
            FileManager fileManager = AppUtil.getBean(FileManager.class);
            // 创建多个有返回值的任务
            List<Future> list = new ArrayList<Future>();
            for (File signatureFile : fs) {
                if (!signatureFile.isDirectory() && signatureFile.getName().endsWith(".png")) {//如果是.png的图片文件则处理
                    Callable c = new MyCallable(signatureFile, user, fileManager);
                    // 执行任务并获取Future对象
                    Future f = pool.submit(c);
                    list.add(f);
                }
            }
            for (Future f : list) {
                UploadResult result = (UploadResult) f.get();
                if (result.isSuccess()) {
                    ObjectNode accountFile = JsonUtil.getMapper().createObjectNode();
                    String[] names = result.getFileName().split("\\.");
                    accountFile.put("account", names[0]);
                    accountFile.put("fileId", result.getFileId());
                    accountFiles.add(accountFile);
                }
            }
            if (accountFiles.size() > 0) {
                ObjectNode signatureData = JsonUtil.getMapper().createObjectNode();
                signatureData.put("repeatConver", isCover);
                signatureData.set("signatureList", accountFiles);
                CommonResult<String> result = ucFeignService.saveSignatureBatch(signatureData);
                //如果签章处理失败，则删除已上传的附件
                if (!result.getState()) {
                    String[] ids = new String[signatureData.size()];
                    for (int i = 0; i < accountFiles.size(); i++) {
                        ids[i] = accountFiles.get(i).get("fileId").asText();
                    }
                    this.delSysFileByIds(ids);
                    return new CommonResult<String>(false, "导入失败：" + result.getMessage());
                }
            }
        } catch (Exception e) {
            message = new CommonResult<String>(false, "导入失败:" + e.getMessage());
        } finally {
            File fileDir = new File(rootRealPath);
            if (fileDir.exists()) {
                FileUtil.deleteDir(fileDir); // 删除解压后的目录
            }
        }
        return message;
    }


    @Override
    public DefaultFile queryByMd5AndSize(MultipartInitParam initParam) {
        if (StringUtil.isEmpty(initParam.getMd5())) {
            throw new RequiredException("文件hash值不能为空！");
        }
        if (BeanUtils.isEmpty(initParam.getSize())) {
            throw new RequiredException("文件长度不能为空！");
        }
        QueryWrapper<DefaultFile> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("md5_value_", initParam.getMd5()).eq("byte_count_", initParam.getSize());
        List<DefaultFile> list = this.baseMapper.selectList(queryWrapper);
        if (list != null && list.size() > 0) {
            if (list.size() == 1) {
                return list.get(0);
            } else {
                throw new ApplicationException("有文件hash值重复的文件！");
            }
        } else {
            DefaultFile sysFile = getDefaultFile(initParam);
            this.save(sysFile);
            return sysFile;
        }
    }

    @NotNull
    private DefaultFile getDefaultFile(MultipartInitParam initParam) {
        DefaultFile sysFile = new DefaultFile();
        sysFile.setId(UniqueIdUtil.getSuid());
        String oriFileName = initParam.getFilename();
        String extName = FileUtil.getFileExt(oriFileName);
        String fileName = sysFile.getId() + "." + extName;
        String suffixName = FileUtils.getSuffixFromFileName(oriFileName);
        String fileType = FileTypeEnum.getType(suffixName);
        sysFile.setFileType(fileType);
        String account = baseContext.getCurrentUserAccout();
        IUser currentUser = userService.getUserByAccount(account);
        String creatorAccount = DefaultFile.FILE_UPLOAD_UNKNOWN;
        // 当前用户的信息
        if (currentUser != null) {
            sysFile.setCreateBy(currentUser.getUserId());
            sysFile.setCreatorName(currentUser.getFullname());
        } else {
            sysFile.setCreatorName(creatorAccount);
        }
        String filePath;
        // 附件名称
        if ("pictureShow".equals(sysFile.getFileType())) {
            filePath = AppFileUtil.createFilePath(creatorAccount + File.separator + "pictureShow", fileName);
        } else {
//            filePath = AppFileUtil.createFilePath(currentUser.getAccount(), fileName);
            filePath=fileName;
        }
        // 附件名称
        sysFile.setFileName(oriFileName.lastIndexOf('.') == -1 ? oriFileName : oriFileName.substring(0, oriFileName.lastIndexOf('.')));
        sysFile.setFilePath(filePath.replaceFirst("^/", ""));
        sysFile.setIsZone(1);
        sysFile.setZoneTotal(initParam.getZoneTotal());
        sysFile.setMd5Value(initParam.getMd5());
        sysFile.setServerLocalName(fileName);
        // 上传时间
        sysFile.setCreateTime(LocalDateTime.now());
        // 扩展名
        sysFile.setExtensionName(extName);
        // 字节总数
        sysFile.setByteCount(initParam.getSize());
        sysFile.setNote(FileUtil.getSize(initParam.getSize()));

        sysFile.setStoreType(initParam.getStoreType());
        return sysFile;
    }

    @Override
    public DefaultFile getFileInfo(FileReqVo fileReqVo) {
        QueryWrapper<DefaultFile> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("FILE_TYPE_", fileReqVo.getFileType());
        queryWrapper.eq("BIZ_ID_", fileReqVo.getBizId());
        DefaultFile defaultFile = this.baseMapper.selectOne(queryWrapper);
        return defaultFile;
    }

    class MyCallable implements Callable<Object> {
        private Logger logger = LoggerFactory.getLogger(FileManagerImpl.class);
        private File file;
        private IUser user;
        private FileManager fileManager;

        MyCallable(File file, IUser user, FileManager fileManager) {
            this.file = file;
            this.user = user;
            this.fileManager = fileManager;
        }

        @Override
        public Object call() throws Exception {
            logger.info(">>>" + file.getName() + "签章附件上传");
            LocalDateTime dateTmp1 = LocalDateTime.now();
            UploadResult result = upload();
            Thread.sleep(200);
            LocalDateTime dateTmp2 = LocalDateTime.now();
            long time = TimeUtil.getTimeMillis(dateTmp2) - TimeUtil.getTimeMillis(dateTmp1);
            logger.info(">>>" + file.getName() + "签章附件上传完成; 花费时间为： " + time);
            return result;
        }

        private UploadResult upload() {
            try {
                FileManager fileManager = AppUtil.getBean(FileManager.class);
                MultipartFile multipartFile = FileUtil.fileToMultipartFile(file);
                List<MultipartFile> files = new ArrayList<MultipartFile>();
                files.add(multipartFile);
                DefaultFile file = new DefaultFile();
                return fileManager.uploadFile(file, files, null, user, "");
            } catch (Exception e) {
                logger.info(">>>" + file.getName() + "签章附件上传失败： " + e.getMessage());
            }
            return null;
        }

    }

    @Override
    public MultipartInitRespVo initMultiPartUpload(MultipartInitParam requestParam) {
        // 文件大小
        long size = requestParam.getSize();
        if (5 * 1024 * 1024 > requestParam.getPartSize()) {
            throw new BaseException("分片大小不能小于5M");
        }
        // 分片数量
        long partCount = size % requestParam.getPartSize() != 0 ? size / requestParam.getPartSize() + 1 : size / requestParam.getPartSize();
        requestParam.setZoneTotal(partCount);
        DefaultFile fileRecord = queryByMd5AndSize(requestParam);
        if (null != fileRecord && !StringUtil.isEmpty(fileRecord.getUploadZoneNum()) && !"1".equals(fileRecord.getMergeStatus())) {
            // redis中缓存默认是24小时失效，url链接24小时失效
            // 在24小时之内再次分片上传都可使用之前分片上传但没都上传成功的分片文件。
            // 考虑上传需要耗时，new Date() - createTime < 20小时才使用之前的结果，否则重新分片初始化
            Date now = new Date();
            // 转换为Instant，默认在系统默认时区
            Instant instant = fileRecord.getCreateTime().atZone(ZoneId.systemDefault()).toInstant();
            // 转换为java.util.Date
            Date date = Date.from(instant);
            long hour = DateUtil.between(date, now, DateUnit.HOUR);
            // TODO 20小时
            if (hour < 20) {
                List<String> partList = new ArrayList<>();
                for (int i = 1; i <= fileRecord.getZoneTotal(); i++) {
                    String chunk = "chunk_" + (i - 1);
                    partList.add(CHUNK_UPLOAD_PREFIX + "?uploadId=" + fileRecord.getServerLocalPath() + "&chunk=" + chunk);
                }
                MultipartInitRespVo multipartInitBO = new MultipartInitRespVo();
                multipartInitBO.setUploadId(fileRecord.getServerLocalPath());
                multipartInitBO.setPartCount(fileRecord.getZoneTotal());
                multipartInitBO.setUploadUrlList(partList);
                String successChunk = getSuccessChunk(multipartInitBO.getUploadId());
                multipartInitBO.setSuccessChunk(successChunk);
                UploadResult result = getUploadResult(fileRecord);
                multipartInitBO.setUploadResult(result);
                return multipartInitBO;
            }
        }

        MultipartInitRespVo multipartInitBO = multipartUpload(requestParam, fileRecord);
        return multipartInitBO;
    }

    private MultipartInitRespVo multipartUpload(MultipartInitParam requestParam, DefaultFile fileRecord) {
        try {
            MultipartInitRespVo multipartInitBO = new MultipartInitRespVo();
            //合并成功的文件 直接将文件信息返回给前端
            if (!StringUtil.isEmpty(fileRecord.getMergeStatus()) && "1".equals(fileRecord.getMergeStatus())) {
                UploadResult result = getUploadResult(fileRecord);
                multipartInitBO.setUploadResult(result);
            } else {
                AttachmentServiceFactory attachmentHandlerFactory = AppUtil.getBean(AttachmentServiceFactory.class);
                attachmentService = attachmentHandlerFactory.getCurrentServices(requestParam.getStoreType());
                fileRecord.setContentType(requestParam.getContentType());
                String uploadId = attachmentService.initMultiPartUpload(fileRecord);
                fileRecord.setServerLocalPath(uploadId);
                this.baseMapper.updateById(fileRecord);
                // 存缓存 uploadId : filePath
                String redisKey = CodePrefix.MULTIPART_UPLOADID_KEY.getKey() + ":" + uploadId;
                // TODO 设置分片上传 uploadId缓存1天
                Long time = 1 * 24 * 60 * 60L;
                redisUtil.set(redisKey, fileRecord.getId(), time);

                Map<String, String> chunkMap = new HashMap<>();
                // 给前端
                List<String> partList = new ArrayList<>();
                for (int i = 1; i <= requestParam.getZoneTotal(); i++) {
                    String uploadUrl = attachmentService.getChunkUrl(fileRecord, i, uploadId);
                    String chunk = "chunk_" + (i - 1);
                    chunkMap.put(chunk, uploadUrl);
                    partList.add(CHUNK_UPLOAD_PREFIX + "?uploadId=" + uploadId + "&chunk=" + chunk);
                    String chunkKey = CodePrefix.MULTIPART_UPLOADID_CHUNK_KEY.getKey() + ":" + uploadId + ":" + chunk;
                    redisUtil.set(chunkKey, uploadUrl, time);
                }
                multipartInitBO.setPartCount(requestParam.getZoneTotal());
                multipartInitBO.setUploadId(uploadId);
                multipartInitBO.setUploadUrlList(partList);
                String successChunk = getSuccessChunk(uploadId);
                multipartInitBO.setSuccessChunk(successChunk);
                UploadResult result = getUploadResult(fileRecord);
                multipartInitBO.setUploadResult(result);
            }
            return multipartInitBO;
        } catch (Exception e) {
            log.error("分片初始化失败，{}", e.getMessage());
            return null;
        }
    }

    @NotNull
    private UploadResult getUploadResult(DefaultFile fileRecord) {
        UploadResult result = new UploadResult();
        result.setSuccess(true);
        result.setFileId(fileRecord.getId());
        result.setFileName(fileRecord.getFileName());
        result.setSize(fileRecord.getByteCount());
        result.setUsername(fileRecord.getCreatorName());
        result.setFileExt(fileRecord.getExtensionName());
        result.setMergeStatus(fileRecord.getMergeStatus());
        result.setFileType(fileRecord.getFileType());
        return result;
    }

    /**
     * 返回当前文件已上传成功的片数信息
     * @param uploadId
     * @return
     */
    private String getSuccessChunk(String uploadId) {
        Map<String, Object> allCaches = redisUtil.getAllCaches(CodePrefix.MULTIPART_UPLOADID_CHUNK_KEY.getKey() + ":" + uploadId + ":");
        StringBuffer sb = new StringBuffer();
        allCaches.forEach((key, value) -> {
            if (null == value) {
                return;
            }
            if ("1".equals(value.toString())) {
                sb.append(",");
                String chunkIndex = key.substring(key.lastIndexOf(":") + 1, key.length());
                sb.append(chunkIndex);
            }
        });
        if (sb.length() > 1) {
            return sb.substring(1);
        }
        return null;
    }

    /**
     * 上传当前分片中的文件 并记录成功分片信息
     * @param file
     * @param uploadId
     * @param chunk
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean chunkUpload(MultipartFile file, String uploadId, String chunk) {
        String chunkKey = CodePrefix.MULTIPART_UPLOADID_CHUNK_KEY.getKey() + ":" + uploadId + ":" + chunk;
        String uploadUrl = redisUtil.get(chunkKey, String.class);
        if (!StringUtil.isEmpty(uploadId) && "1".equals(uploadUrl)) {
            // 断点续传
            return true;
        }

        String redisKey = CodePrefix.MULTIPART_UPLOADID_KEY.getKey() + ":" + uploadId;
        String fileId = redisUtil.get(redisKey, String.class);
        Assert.hasText(fileId, "分片文件不存在");
        DefaultFile defaultFile = this.get(fileId);
        Assert.notNull(defaultFile, "分片文件不存在");
        String msg = putMethod(uploadUrl, file);
        //分片上传成功后 在缓存中进行记录 并记录成功的片数总和
        if (null == msg) {
            Long expire = redisUtil.getExpire(chunkKey);
            // chunk已经上传成功
            redisUtil.set(chunkKey, "1", expire.intValue());
            String chunkNumKey = CodePrefix.MULTIPART_UPLOADID_CHUNKNUM_KEY.getKey() + ":" + uploadId;
            long incr = redisUtil.incr(chunkNumKey, 1);
            defaultFile.setUploadZoneNum(incr);
        }
        boolean update = this.updateById(defaultFile);

        return update;
    }

    /**
     * 当所有分片上传成功后 进行分片合并
     * @param param
     * @return
     */
    @Override
    public UploadResult mergeMultipartUpload(MultipartCompleteParam param) {
        String redisKey = CodePrefix.MULTIPART_UPLOADID_KEY.getKey() + ":" + param.getUploadId();
        String fileId = redisUtil.get(redisKey, String.class);
        Assert.hasText(fileId, "分片文件不存在");
        DefaultFile defaultFile = this.get(fileId);
        Assert.notNull(defaultFile, "分片文件不存在");
        //如果所有分片上传完成则进行分片合并并修改分片文件合并状态
        if (defaultFile.getUploadZoneNum().equals(defaultFile.getZoneTotal())) {
            mergeMultipartUpload(param.getUploadId(), defaultFile);
        }
        UploadResult result = null;
        boolean update = this.updateById(defaultFile);
        if (update) {
            result = new UploadResult();
            result.setSuccess(true);
            result.setFileId(defaultFile.getId());
            result.setFileName(defaultFile.getFileName());
            result.setSize(defaultFile.getByteCount());
            result.setUsername(defaultFile.getCreatorName());
            result.setFileExt(defaultFile.getExtensionName());
            result.setMergeStatus(defaultFile.getMergeStatus());
            result.setFileType(defaultFile.getFileType());
            //如果是视频文件则异步进行转M3U8，并记录M3U8文件上传地址
            if (FileTypeEnum.video.getType().equals(result.getFileType())) {
                executorService.execute(() -> {
                    UploadM3u8 uploadM3u8 = new UploadM3u8();
                    String url = uploadM3u8.uploadVideoToM3U8(defaultFile);
                    defaultFile.setProp4(url);
                    this.updateById(defaultFile);
                    log.info("视屏转换成功!");
                });
            }
            //如果是文档且不是pdf文件 则异步转换为pdf文档
            if (FileTypeEnum.txt.getType().equals(result.getFileType()) && !"pdf".equals(result.getFileExt())) {
                executorService.execute(() -> {
                    Map<String, String> map = new HashMap<String, String>();
                    //文档转pdf
                    FilePreview filePreview = previewFactory.get(defaultFile);
                    filePreview.filePreviewHandle(defaultFile, map);
                });

            }
        }

        return result;
    }

    @Override
    public JSONObject minioUpload(MultipartFile file, String bizPath, HttpServletResponse response) throws Exception {
        JSONObject accessory = new JSONObject();
        String fileName = null;
        if (file.isEmpty() || file.getSize() == 0) {
            throw new BaseException("文件为空");
        }
        if (StringUtils.isBlank(fileName)) {
            fileName = file.getOriginalFilename();
        }
        accessory.put("name",fileName);
        String name = UUID.randomUUID().toString() + fileName.substring(fileName.lastIndexOf("."));
        //如果为空放到默认文件夹
        if (StringUtils.isBlank(bizPath)){
            bizPath="default";
        }
        String url = MinioUtil.upload(file, bizPath, name, "xcjy");
        //验证图片类型
        String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
        accessory.put("type",ext);
        accessory.put("suffix",ext);
        accessory.put("url",url);
        accessory.put("isDele","0");
        return accessory;
    }

    @Override
    public String minioPreviewFile(String bucketName, String fileName) throws Exception {
        String fileUrl = MinioUtil.previewFile(bucketName, fileName, null, null);
        return fileUrl;
    }

    @Override
    public String minioPreviewFile(String id) throws Exception {
        DefaultFile file = baseMapper.selectById(id);
        if (file!=null){
            String fileUrl = MinioUtil.previewFile(null, file.getFilePath(), null, null);
            return fileUrl;
        }
        return "";
    }

    public void mergeMultipartUpload(String uploadId, DefaultFile defaultFile) {
        boolean status = false;
        try {
            String redisKey = CodePrefix.MULTIPART_UPLOADID_KEY.getKey() + ":" + uploadId;
            String fileId = redisUtil.get(redisKey, String.class);
            if (StringUtil.isEmpty(fileId)) {
                throw new BaseException("分片上传失败！");
            }

            String chunkListKeyPattern = CodePrefix.MULTIPART_UPLOADID_CHUNK_KEY.getKey() + ":" + uploadId + ":chunk_*";
            Object data = redisUtil.getAllCache(chunkListKeyPattern);
            if (null == data) {
                // 缓存过期,不能合并
                log.info(">>>>>>>>>>>{}缓存过期，不能合并", uploadId);
                throw new BaseException("分片上传失败！");
            }
            int realyPartNumber = defaultFile.getUploadZoneNum().intValue();

            AttachmentServiceFactory attachmentHandlerFactory = AppUtil.getBean(AttachmentServiceFactory.class);
            attachmentService = attachmentHandlerFactory.getCurrentServices(defaultFile.getStoreType());
            // 合并成功，isUPloaded设为1，删除redis缓存
            status = attachmentService.mergeMultipartUpload(defaultFile, uploadId, realyPartNumber);
            if (status) {
                defaultFile.setMergeStatus("1");
                redisUtil.del(redisKey);
                redisUtil.delLike(chunkListKeyPattern);
            }
        } catch (Exception e) {
            log.error("分片合并失败，{}", e.fillInStackTrace().toString());
        } finally {
            log.info(">>>>>>>>>>>>合并chunk,uploadId:{}, 合并结果：{}", uploadId, status);
        }
    }

    /**
     * 向minio上传分片文件
     *
     * @param url
     * @param file
     * @return
     */
    private String putMethod(String url, MultipartFile file) {
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        CloseableHttpResponse response = null;
        try {
            HttpPut httpPut = new HttpPut(url);
            ByteArrayEntity byteArrayEntity = new ByteArrayEntity(file.getBytes());
            httpPut.setEntity(byteArrayEntity);
            response = httpClient.execute(httpPut);
            log.info(">>>>>>>>>>>>>>" + url + "的响应状态为:{}, resonPhrase: {}", response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase());
            if (response.getStatusLine().getStatusCode() != 200) {
                return "上传失败";
            }
            return null;
        } catch (Exception e) {
            e.printStackTrace();
            return "上传失败";
        } finally {
            try {
                // 释放资源
                if (httpClient != null) {
                    httpClient.close();
                }
                if (response != null) {
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
