接收文件(文件上传)

时间:2024-05-07 21:03:16

前端的请求内容

Content-Type: multipart/form-data;

form-data; name="filedata"; filename="具体的文件名称"

后端定义接口

    @ApiOperation("文件上传接口")
    @RequestMapping(value = "/upload/coursefile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public UploadFileResultDto upload(@RequestPart("filedata") MultipartFile multipartFile){

        Long companyId=2L;
        try {
            //创建临时文件
            File tempFile = tempFile = File.createTempFile("minio", "temp");
            //上传的文件拷贝到临时文件
            multipartFile.transferTo(tempFile);
            UploadFileParamsDto uploadFileParamsDto = new UploadFileParamsDto();
            uploadFileParamsDto.setFilename(multipartFile.getOriginalFilename());
            uploadFileParamsDto.setFileSize(multipartFile.getSize());
            uploadFileParamsDto.setFileType("001001");
            String absolutePath = tempFile.getAbsolutePath();
            UploadFileResultDto uploadFileResultDto =                                 mediaFileService.uploadFile(companyId, uploadFileParamsDto, absolutePath);
            return  uploadFileResultDto;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

 使用minio来完成业务逻辑

minio介绍: 

分布式文件系统minio-****博客

获取文件默认目录名 

//获取文件默认目录名
    private String getDefaultFolderPath(){
        //根据时间来生产存储目录
        LocalDate now = LocalDate.now();
        String defaultFolderPath = now.toString().replaceAll("-", "/")+"/";
        return defaultFolderPath;
    }

获取文件md5值 

//获取文件md5值
    private  String getFileMd5(File file){
        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            String md5 = DigestUtils.md5Hex(fileInputStream);
            return md5;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

 根据扩展名来获取文件类型

private  String getMimeType(String extension){
            if(extension==null)
                extension = "";
            //根据扩展名取出mimeType
            ContentInfo extensionMatch = ContentInfoUtil.findExtensionMatch(extension);
            //通用mimeType,字节流
            String mimeType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
            if(extensionMatch!=null){
                mimeType = extensionMatch.getMimeType();
            }
            return mimeType;

    }

上传到数据库

  /**
     * @description 将文件信息添加到文件表
     * @param companyId  机构id
     * @param fileMd5  文件md5值
     * @param uploadFileParamsDto  上传文件的信息
     * @param bucket  桶
     * @param objectName 对象名称
     * @return com.xuecheng.media.model.po.MediaFiles
     * @author Mr.M
     * @date 2022/10/12 21:22
     */
    @Transactional
    public MediaFiles addMediaFilesToDb(Long companyId,String fileMd5,QueryMediaParamsDto uploadFileParamsDto,String bucket,String objectName){
        //从数据库查询文件
        MediaFiles mediaFiles = mediaFilesMapper.selectById(fileMd5);
        if (mediaFiles == null) {
            mediaFiles = new MediaFiles();
            //拷贝基本信息
            BeanUtils.copyProperties(uploadFileParamsDto, mediaFiles);
            mediaFiles.setId(fileMd5);
            mediaFiles.setFileId(fileMd5);
            mediaFiles.setCompanyId(companyId);
            mediaFiles.setUrl("/" + bucket + "/" + objectName);
            mediaFiles.setBucket(bucket);
            mediaFiles.setFilePath(objectName);
            mediaFiles.setCreateDate(LocalDateTime.now());
            mediaFiles.setAuditStatus("002003");
            mediaFiles.setStatus("1");
            //保存文件信息到文件表
            int insert = mediaFilesMapper.insert(mediaFiles);
            if (insert < 0) {
                log.error("保存文件信息到数据库失败,{}",mediaFiles.toString());
                XueChengPlusException.cast("保存文件信息失败");
            }
            log.debug("保存文件信息到数据库成功,{}",mediaFiles.toString());

        }
        return mediaFiles;

    }

service:

 @Autowired
    MediaFilesMapper mediaFilesMapper;
    //普通文件桶
    @Value("${minio.bucket.files}")
    String bucket_Files;
    @Autowired
    MinioClient minioClient;
 /**
     * 普通文件上传
     * @param companyId 机构id
     * @param queryMediaParamsDto 上传文件信息
     * @param localFilePath 文件磁盘路径
     * @return
     */
    @Override
    public UploadFileResultDto uploadFile(Long companyId, QueryMediaParamsDto queryMediaParamsDto, String localFilePath) {

        //根据文件路径获取文件对象
        File file = new File(localFilePath);
        if (file==null){
            XueChengPlusException.cast("文件不存在");
        }
        //获取文件名称
        String filename = queryMediaParamsDto.getFilename();
        //获取文件拓展名
        String extension = filename.substring(filename.lastIndexOf("."));
        //获取默认存储目录
        String defaultFolderPath = getDefaultFolderPath();
        //获取文件的md5值
        String fileMd5 = getFileMd5(file);
        //获取文件类型
        String mimeType = getMimeType(extension);

        //存储到minio中的对象名(带目录)
        String objectName = defaultFolderPath+fileMd5+extension;
        //上传到minio
        try {
            UploadObjectArgs uploadObjectArgs = UploadObjectArgs.builder()
                    .bucket(bucket_Files)
                    .object(objectName)//添加子目录
                    .filename(localFilePath)
                    .contentType(mimeType)//默认根据扩展名确定文件内容类型,也可以指定
                    .build();
            minioClient.uploadObject(uploadObjectArgs);
            log.debug("上传文件到minio成功,bucket:{},objectName:{}",bucket_Files,objectName);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("上传文件到minio出错,bucket:{},objectName:{},错误原因:{}",bucket_Files,objectName,e.getMessage(),e);
            XueChengPlusException.cast("上传文件到文件系统失败");
        }
        //将信息存储到数据库中
        MediaFiles mediaFiles = addMediaFilesToDb(companyId, fileMd5, queryMediaParamsDto, bucket_Files, objectName);
        UploadFileResultDto uploadFileResultDto = new UploadFileResultDto();
        BeanUtils.copyProperties(mediaFiles,uploadFileResultDto);
        return uploadFileResultDto;
    }

事务控制优化

在测试中我发现addMediaFilesToDb上加的事务控制失效了:

当一个非事务方法调用同一个类中的事务方法时,事务无法按预期控制的原因通常与事务管理器的代理机制有关。在Spring框架中,事务管理通常是通过AOP(面向切面编程)实现的,这意味着事务是通过动态代理来控制的。

具体来说,Spring使用@TransactionInterceptor来拦截对事务方法的调用,并在方法执行前后添加事务管理的逻辑。但是,这种代理通常只会在外部类(或Spring容器外部)对目标方法进行调用时生效。如果同一个类中的非事务方法直接调用事务方法,那么代理机制不会被触发,因为内部调用会绕过Spring容器注入的代理对象,直接调用目标对象的实例方法。

 总结:当使用代理对象时@TransactionInterceptor才会进行事务

解决方法:

将事务方法移动到一个不同的Spring Bean中,并确保从原始Bean中通过Spring容器注入该Bean的引用。这样,当原始Bean中的非事务方法调用该Bean的事务方法时,Spring的代理机制将被触发,从而应用事务管理。

将bean注入进来,保证事务方法由代理对象来执行

 @Autowired
    MediaFileService currentProxy;

 把addMediaFilesToDb提到接口中

 

public MediaFiles addMediaFilesToDb
(Long companyId, String fileMd5, UploadFileParamsDto uploadFileParamsDto, String bucket, String objectName);

 修改serviceimpl

MediaFiles mediaFiles = 
currentProxy.addMediaFilesToDb(companyId, fileMd5, uploadFileParamsDto, bucket_files, objectName);