Browse Source

3.3.0 图片上传方法

xiaofei 1 year ago
parent
commit
003bad747e

+ 5 - 6
teachcloud-common/src/main/java/com/qmth/teachcloud/common/config/DictionaryConfig.java

@@ -1,7 +1,6 @@
 package com.qmth.teachcloud.common.config;
 
-import com.qmth.teachcloud.common.domain.FssPrivateDomain;
-import com.qmth.teachcloud.common.domain.FssPublicDomain;
+import com.qmth.teachcloud.common.domain.FssDomain;
 import com.qmth.teachcloud.common.domain.SysDomain;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.context.annotation.Bean;
@@ -30,13 +29,13 @@ public class DictionaryConfig {
 
     @Bean
     @ConfigurationProperties(prefix = "com.qmth.fss.public", ignoreUnknownFields = false)
-    public FssPublicDomain fssPublicDomain() {
-        return new FssPublicDomain();
+    public FssDomain fssPublicDomain() {
+        return new FssDomain();
     }
 
     @Bean
     @ConfigurationProperties(prefix = "com.qmth.fss.private", ignoreUnknownFields = false)
-    public FssPrivateDomain fssPrivateDomain() {
-        return new FssPrivateDomain();
+    public FssDomain fssPrivateDomain() {
+        return new FssDomain();
     }
 }

+ 40 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/domain/FssDomain.java

@@ -0,0 +1,40 @@
+package com.qmth.teachcloud.common.domain;
+
+import com.qmth.teachcloud.common.contant.SystemConstant;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ *  文件存储中心配置
+ */
+public class FssDomain {
+    private static final long serialVersionUID = 7510626406622200443L;
+
+    private String config;
+
+    private String server;
+
+    public static long getSerialVersionUID() {
+        return serialVersionUID;
+    }
+
+    public String getConfig() {
+        return config;
+    }
+
+    public void setConfig(String config) {
+        this.config = config;
+    }
+
+    public String getServer() {
+        if (!StringUtils.isBlank(this.server) && this.server.endsWith(SystemConstant.ORG_SPLIT)) {
+            this.server = this.server.substring(0, this.server.length() - 1);
+            return this.server;
+        } else {
+            return this.server;
+        }
+    }
+
+    public void setServer(String server) {
+        this.server = server;
+    }
+}

+ 19 - 6
teachcloud-common/src/main/java/com/qmth/teachcloud/common/enums/UploadFileEnum.java

@@ -1,5 +1,7 @@
 package com.qmth.teachcloud.common.enums;
 
+import org.apache.commons.lang3.StringUtils;
+
 import java.util.Objects;
 
 /**
@@ -35,17 +37,21 @@ public enum UploadFileEnum {
      */
     HTML("html", "private", ""),
     /**
-     * 阅卷考生题卡原图
+     * 阅卷考生题卡原图(path:{uploadType}/{examId}/{coursePaperId}/{studentCode}/时间戳-{pageIndex}.jpg)
+     */
+    SHEET("sheet", "private", "%s/%d/%s/%s/%d-%d.%s"),
+    /**
+     * 卡格式文件(path:{uploadType}/{examId}/{coursePaperId}/{number}.json)
      */
-    SHEET("sheet", "private", ""),
+    CARD("card", "private", "%s/%d/%s/%d.%s"),
     /**
-     * 卡格式文件
+     * 适配后卡格式文件(path:{uploadType}/{examId}/{coursePaperId}/adapt/{number}.json)
      */
-    CARD("card", "private", ""),
+    ADAPT("adapt", "private", "%s/%d/%s/adapt/%d.%s"),
     /**
-     * 适配后卡格式文件
+     * 签到表文件(path:{uploadType}/{examId}/{coursePaperId}/{packageCode}-{packageNo}.jpg)
      */
-    ADAPTE("adapte", "private", "");
+    PACKAGE("package", "private", "%s/%d/%s/%s-%s.%s");
 
     private String title;
     private String fssType;
@@ -98,4 +104,11 @@ public enum UploadFileEnum {
         }
         return null;
     }
+
+    public String getPath(Object... param) {
+        if (StringUtils.isBlank(pattern)) {
+            return null;
+        }
+        return String.format(pattern, param);
+    }
 }

+ 4 - 2
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/bean/FilePathVo.java

@@ -1,5 +1,7 @@
 package com.qmth.teachcloud.mark.bean;
 
+import com.qmth.teachcloud.common.enums.UploadFileEnum;
+
 public class FilePathVo {
 
     private String path;
@@ -9,9 +11,9 @@ public class FilePathVo {
     public FilePathVo() {
     }
 
-    public FilePathVo(String path, String uploadType, String type) {
+    public FilePathVo(String path, UploadFileEnum uploadType, String type) {
         this.path = path;
-        this.uploadType = uploadType;
+        this.uploadType = uploadType.name();
         this.type = type;
     }
 

+ 5 - 5
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/FileService.java

@@ -4,17 +4,17 @@ import java.io.InputStream;
 
 public interface FileService {
 
-	String getAnswerCardUri(Long examId, Integer number);
+	String getAnswerCardUri(Long examId, String coursePaperId, Integer number);
 
-	String uploadAnswerCard(InputStream in, String md5, Long examId, Integer number);
+	String uploadAnswerCard(InputStream in, String md5, Long examId, String coursePaperId, Integer number);
 
-	String getAdapteFileUri(Long examId, Integer cardNumber);
+	String getAdapteFileUri(Long examId, String coursePaperId, Integer cardNumber);
 
-	String uploadAdapteFile(InputStream in, String md5, Long examId, Integer number);
+	String uploadAdapteFile(InputStream in, String md5, Long examId, String coursePaperId, Integer number);
 	
 	 String uploadSheet(InputStream ins, String md5, Long examId, Long batchId, String coursePaperId,String studentCode, Integer paperNumber,
 	            Integer pageIndex);
 
-	String getSheetUri(Long examId, Long batchId, String coursePaperId,String studentCode, Integer paperNumber, Integer pageIndex);
+	String getSheetUri(Long examId, String coursePaperId, String studentCode, Integer paperNumber, Integer pageIndex);
 
 }

+ 79 - 83
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/FileServiceImpl.java

@@ -1,99 +1,95 @@
 package com.qmth.teachcloud.mark.service.impl;
 
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
 import com.qmth.boot.core.exception.StatusException;
-import com.qmth.boot.core.fss.store.FileStore;
-import com.qmth.boot.tools.uuid.FastUUID;
-import com.qmth.teachcloud.mark.enums.FileType;
+import com.qmth.teachcloud.common.enums.UploadFileEnum;
 import com.qmth.teachcloud.mark.enums.FormatType;
 import com.qmth.teachcloud.mark.service.FileService;
+import com.qmth.teachcloud.mark.utils.FileStoreUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.io.InputStream;
 
 @Service
 public class FileServiceImpl implements FileService {
 
-//	@Autowired
-	private FileStore fileStore;
+    @Autowired
+    private FileStoreUtils fileStoreUtils;
 
-	@Override
-	public String getAnswerCardUri(Long examId, Integer number) {
-		return FileType.CARD.getPath(examId, number, FormatType.JSON.name().toLowerCase());
-	}
+    @Override
+    public String getAnswerCardUri(Long examId, String coursePaperId, Integer number) {
+        return UploadFileEnum.CARD.getPath(UploadFileEnum.CARD.getTitle(), examId, coursePaperId, number, FormatType.JSON.name().toLowerCase());
+    }
 
-	@Override
-	public String uploadAnswerCard(InputStream in, String md5, Long examId, Integer number) {
-		String path = getAnswerCardUri(examId, number);
-		try {
-			fileStore.write(path, in, md5);
-			return path;
-		} catch (RuntimeException e) {
-			throw e;
-		} catch (Exception e) {
-			throw new StatusException("文件上传出错:" + path, e);
-		} finally {
-			if (in != null) {
-				try {
-					in.close();
-				} catch (IOException e) {
-				}
-			}
-		}
-	}
+    @Override
+    public String uploadAnswerCard(InputStream in, String md5, Long examId, String coursePaperId, Integer number) {
+        String path = null;
+        try {
+            path = fileStoreUtils.uploadFileByPattern(in, md5, UploadFileEnum.CARD, path);
+            return fileStoreUtils.filePreview(path);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new StatusException("文件上传出错:" + path, e);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
 
-	@Override
-	public String uploadAdapteFile(InputStream in, String md5, Long examId, Integer number) {
-		String path = getAdapteFileUri(examId, number);
-		try {
-			fileStore.write(path, in, md5);
-			return path;
-		} catch (RuntimeException e) {
-			throw e;
-		} catch (Exception e) {
-			throw new StatusException("文件上传出错:" + path, e);
-		} finally {
-			if (in != null) {
-				try {
-					in.close();
-				} catch (IOException e) {
-				}
-			}
-		}
-	}
+    @Override
+    public String uploadAdapteFile(InputStream in, String md5, Long examId, String coursePaperId, Integer number) {
+        String path = getAdapteFileUri(examId, coursePaperId, number);
+        try {
+            path = fileStoreUtils.uploadFileByPattern(in, md5, UploadFileEnum.ADAPT, path);
+            return fileStoreUtils.filePreview(path);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new StatusException("文件上传出错:" + path, e);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
 
-	@Override
-	public String getAdapteFileUri(Long examId, Integer cardNumber) {
-		return FileType.ADAPTE_FILE.getPath(examId, cardNumber, FastUUID.get(), FormatType.JSON.name().toLowerCase());
-	}
+    @Override
+    public String getAdapteFileUri(Long examId, String coursePaperId, Integer cardNumber) {
+        return UploadFileEnum.ADAPT.getPath(UploadFileEnum.ADAPT.getTitle(), examId, coursePaperId, cardNumber, FormatType.JSON.name().toLowerCase());
+    }
 
-	@Override
-	public String uploadSheet(InputStream in, String md5, Long examId, Long batchId, String coursePaperId,
-			String studentCode, Integer paperNumber, Integer pageIndex) {
-		String path = getSheetUri(examId, batchId, coursePaperId,studentCode, paperNumber, pageIndex);
-		try {
-			fileStore.write(path, in, md5);
-			return path;
-		} catch (RuntimeException e) {
-			throw e;
-		} catch (Exception e) {
-			throw new StatusException("文件上传出错:"+path, e);
-		} finally {
-			if (in != null) {
-				try {
-					in.close();
-				} catch (IOException e) {
-				}
-			}
-		}
-	}
+    @Override
+    public String uploadSheet(InputStream in, String md5, Long examId, Long batchId, String coursePaperId,
+                              String studentCode, Integer paperNumber, Integer pageIndex) {
+        String path = getSheetUri(examId, coursePaperId, studentCode, paperNumber, pageIndex);
+        try {
+            path = fileStoreUtils.uploadFileByPattern(in, md5, UploadFileEnum.SHEET, path);
+            return fileStoreUtils.filePreview(path);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new StatusException("文件上传出错:" + path, e);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
 
-	@Override
-	public String getSheetUri(Long examId, Long batchId, String coursePaperId, String studentCode, Integer paperNumber,
-			Integer pageIndex) {
-		return FileType.SHEET.getPath(examId, batchId, coursePaperId, studentCode, paperNumber, pageIndex,
-				FormatType.JPG.name().toLowerCase());
-	}
+    @Override
+    public String getSheetUri(Long examId, String coursePaperId, String studentCode, Integer paperNumber, Integer pageIndex) {
+        return UploadFileEnum.SHEET.getPath(UploadFileEnum.SHEET.getTitle(), examId, coursePaperId, studentCode, paperNumber, pageIndex, FormatType.JPG.name().toLowerCase());
+    }
 }

+ 154 - 155
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/ScanAnswerCardServiceImpl.java

@@ -39,125 +39,124 @@ import com.qmth.teachcloud.mark.service.ScanPaperService;
  */
 @Service
 public class ScanAnswerCardServiceImpl extends ServiceImpl<ScanAnswerCardMapper, ScanAnswerCard>
-		implements ScanAnswerCardService {
-
-	@Autowired
-	private MarkPaperService markPaperService;
-	
-	@Autowired
-	private ScanPaperService scanPaperService;
-
-	@Autowired
-	private FileService fileService;
-
-	@Override
-	public ScanAnswerCard findByExamAndNumber(Long examId, Integer cardNumber) {
-		if (examId == null) {
-			throw new ParameterException("examId不能为空");
-		}
-		if (cardNumber == null) {
-			throw new ParameterException("cardNumber不能为空");
-		}
-		QueryWrapper<ScanAnswerCard> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<ScanAnswerCard> lw = wrapper.lambda();
-		lw.eq(ScanAnswerCard::getExamId, examId);
-		lw.eq(ScanAnswerCard::getNumber, cardNumber);
-		return baseMapper.selectOne(wrapper);
-	}
-
-	@Override
-	public List<AnswerCardVo> cardList(Long examId, String coursePaperId) {
-		return baseMapper.cardList(examId, coursePaperId);
-	}
-
-	@Transactional
-	@Override
-	public void cardSave(AnswerCardSaveDomain domain) {
-		boolean singlePage = false;
-		MarkPaper mp = markPaperService.getByExamIdAndCoursePaperId(domain.getExamId(), domain.getCoursePaperId());
-		if (MarkPaperStatus.FINISH.equals(mp.getStatus())) {
-			throw new ParameterException("阅卷已结束");
-		}
-		ScanAnswerCard card = null;
-		if (domain.getNumber() != null) {
-			card = findByExamAndNumber(domain.getExamId(), domain.getNumber());
-			if (card == null) {
-				throw new ParameterException("未找到卡格式信息");
-			}
-		} else {
-			card = new ScanAnswerCard();
-			card.setExamId(domain.getExamId());
-			card.setCoursePaperId(domain.getCoursePaperId());
-			card.setPaperCount(domain.getPaperCount());
-			card.setSinglePage(singlePage);
-			card.setRemark(domain.getRemark());
-			card.setDpi(domain.getDpi());
-			card.setSource(CardSource.CLIENT);
-			card.setNumber(findMaxCardNumberByExamId(domain.getExamId()) + 1);
-			card.setNeedAdapte(false);
-		}
-		CardFile cardFile;
-		byte[] fileData;
-		String sliceConfig;
-		try {
-			fileData = domain.getFile().getBytes();
-			// 解析卡格式文件
-			cardFile = parseCardFile(fileData);
-			if (singlePage && domain.getPaperCount() != cardFile.getPages().size()) {
-				throw new ParameterException("卡格式数量不一致");
-			}
-			if (!singlePage && domain.getPaperCount() * 2 != cardFile.getPages().size()) {
-				throw new ParameterException("卡格式数量不一致");
-			}
-			// 提取裁切坐标
-			sliceConfig = cardFile.getSliceConfig().toString();
-		} catch (IOException e) {
-			throw new ParameterException("文件解析失败", e);
-		}
-		String filePath = null;
-		try {
-			fileService.uploadAnswerCard(domain.getFile().getInputStream(), domain.getMd5(), domain.getExamId(),
-					domain.getNumber());
-		} catch (IOException e) {
-			throw new ParameterException("文件上传失败", e);
-		}
-		card.setSliceConfig(sliceConfig);
-		card.setMd5(domain.getMd5());
-		card.setUri(filePath);
-		this.saveOrUpdate(card);
-	}
-
-	/**
-	 * 解析卡格式文件中的裁切图坐标,用于云阅卷同步
-	 *
-	 * @param data
-	 */
-	private CardFile parseCardFile(byte[] data) throws IOException {
-		ObjectMapper objectMapper = new ObjectMapper();
-		return objectMapper.readValue(data, CardFile.class);
-	}
-
-	private Integer findMaxCardNumberByExamId(Long examId) {
-		int number = 0;
-		QueryWrapper<ScanAnswerCard> queryWrapper = new QueryWrapper<>();
-		queryWrapper.lambda().eq(ScanAnswerCard::getExamId, examId);
-		List<ScanAnswerCard> list = this.list(queryWrapper);
-		for (ScanAnswerCard card : list) {
-			if (number < card.getNumber()) {
-				number = card.getNumber();
-			}
-		}
-		return number;
-	}
-
-	@Transactional
-	@Override
-	public void cardDelete(Long examId, Integer number) {
-		ScanAnswerCard card = findByExamAndNumber(examId, number);
-		if (card == null) {
-			throw new ParameterException("未找到卡格式信息");
-		}
-		int studentCount = scanPaperService.getCountByExamAndCardNumber(examId, number);
+        implements ScanAnswerCardService {
+
+    @Autowired
+    private MarkPaperService markPaperService;
+
+    @Autowired
+    private ScanPaperService scanPaperService;
+
+    @Autowired
+    private FileService fileService;
+
+    @Override
+    public ScanAnswerCard findByExamAndNumber(Long examId, Integer cardNumber) {
+        if (examId == null) {
+            throw new ParameterException("examId不能为空");
+        }
+        if (cardNumber == null) {
+            throw new ParameterException("cardNumber不能为空");
+        }
+        QueryWrapper<ScanAnswerCard> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<ScanAnswerCard> lw = wrapper.lambda();
+        lw.eq(ScanAnswerCard::getExamId, examId);
+        lw.eq(ScanAnswerCard::getNumber, cardNumber);
+        return baseMapper.selectOne(wrapper);
+    }
+
+    @Override
+    public List<AnswerCardVo> cardList(Long examId, String coursePaperId) {
+        return baseMapper.cardList(examId, coursePaperId);
+    }
+
+    @Transactional
+    @Override
+    public void cardSave(AnswerCardSaveDomain domain) {
+        boolean singlePage = false;
+        MarkPaper mp = markPaperService.getByExamIdAndCoursePaperId(domain.getExamId(), domain.getCoursePaperId());
+        if (MarkPaperStatus.FINISH.equals(mp.getStatus())) {
+            throw new ParameterException("阅卷已结束");
+        }
+        ScanAnswerCard card = null;
+        if (domain.getNumber() != null) {
+            card = findByExamAndNumber(domain.getExamId(), domain.getNumber());
+            if (card == null) {
+                throw new ParameterException("未找到卡格式信息");
+            }
+        } else {
+            card = new ScanAnswerCard();
+            card.setExamId(domain.getExamId());
+            card.setCoursePaperId(domain.getCoursePaperId());
+            card.setPaperCount(domain.getPaperCount());
+            card.setSinglePage(singlePage);
+            card.setRemark(domain.getRemark());
+            card.setDpi(domain.getDpi());
+            card.setSource(CardSource.CLIENT);
+            card.setNumber(findMaxCardNumberByExamId(domain.getExamId()) + 1);
+            card.setNeedAdapte(false);
+        }
+        CardFile cardFile;
+        byte[] fileData;
+        String sliceConfig;
+        try {
+            fileData = domain.getFile().getBytes();
+            // 解析卡格式文件
+            cardFile = parseCardFile(fileData);
+            if (singlePage && domain.getPaperCount() != cardFile.getPages().size()) {
+                throw new ParameterException("卡格式数量不一致");
+            }
+            if (!singlePage && domain.getPaperCount() * 2 != cardFile.getPages().size()) {
+                throw new ParameterException("卡格式数量不一致");
+            }
+            // 提取裁切坐标
+            sliceConfig = cardFile.getSliceConfig().toString();
+        } catch (IOException e) {
+            throw new ParameterException("文件解析失败", e);
+        }
+        String filePath = null;
+        try {
+            fileService.uploadAnswerCard(domain.getFile().getInputStream(), domain.getMd5(), domain.getExamId(), domain.getCoursePaperId(), domain.getNumber());
+        } catch (IOException e) {
+            throw new ParameterException("文件上传失败", e);
+        }
+        card.setSliceConfig(sliceConfig);
+        card.setMd5(domain.getMd5());
+        card.setUri(filePath);
+        this.saveOrUpdate(card);
+    }
+
+    /**
+     * 解析卡格式文件中的裁切图坐标,用于云阅卷同步
+     *
+     * @param data
+     */
+    private CardFile parseCardFile(byte[] data) throws IOException {
+        ObjectMapper objectMapper = new ObjectMapper();
+        return objectMapper.readValue(data, CardFile.class);
+    }
+
+    private Integer findMaxCardNumberByExamId(Long examId) {
+        int number = 0;
+        QueryWrapper<ScanAnswerCard> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(ScanAnswerCard::getExamId, examId);
+        List<ScanAnswerCard> list = this.list(queryWrapper);
+        for (ScanAnswerCard card : list) {
+            if (number < card.getNumber()) {
+                number = card.getNumber();
+            }
+        }
+        return number;
+    }
+
+    @Transactional
+    @Override
+    public void cardDelete(Long examId, Integer number) {
+        ScanAnswerCard card = findByExamAndNumber(examId, number);
+        if (card == null) {
+            throw new ParameterException("未找到卡格式信息");
+        }
+        int studentCount = scanPaperService.getCountByExamAndCardNumber(examId, number);
         if (studentCount != 0) {
             throw new ParameterException("卡格式已被使用,无法删除");
         }
@@ -168,43 +167,43 @@ public class ScanAnswerCardServiceImpl extends ServiceImpl<ScanAnswerCardMapper,
         if (count == 0) {
             throw new ParameterException("未找到卡格式");
         }
-	}
-
-	@Transactional
-	@Override
-	public UriVo adapteUpload(Long examId, String coursePaperId, Integer cardNumber, String md5, Integer dpi,
-			MultipartFile file, String remark) {
-		MarkPaper mp = markPaperService.getByExamIdAndCoursePaperId(examId,coursePaperId);
-		if (MarkPaperStatus.FINISH.equals(mp.getStatus())) {
-			throw new ParameterException("阅卷已结束");
-		}
-		ScanAnswerCard card = findByExamAndNumber(examId, cardNumber);
-		if (card == null) {
-			throw new ParameterException("未找到卡格式信息");
-		}
-		if (!card.getNeedAdapte()) {
+    }
+
+    @Transactional
+    @Override
+    public UriVo adapteUpload(Long examId, String coursePaperId, Integer cardNumber, String md5, Integer dpi,
+                              MultipartFile file, String remark) {
+        MarkPaper mp = markPaperService.getByExamIdAndCoursePaperId(examId, coursePaperId);
+        if (MarkPaperStatus.FINISH.equals(mp.getStatus())) {
+            throw new ParameterException("阅卷已结束");
+        }
+        ScanAnswerCard card = findByExamAndNumber(examId, cardNumber);
+        if (card == null) {
+            throw new ParameterException("未找到卡格式信息");
+        }
+        if (!card.getNeedAdapte()) {
             throw new ParameterException("卡格式无需适配");
         }
-		String filePath = null;
-		try {
-			fileService.uploadAdapteFile(file.getInputStream(), md5, examId, cardNumber);
-		} catch (IOException e) {
-			throw new ParameterException("文件上传失败", e);
-		}
-		card.setAdapteMd5(md5);
-		card.setAdapteUri(filePath);
-		UriVo vo=new UriVo();
-		vo.setUri(filePath);
-		return vo;
-	}
-
-	@Override
-	public AnswerCardVo cardGet(Long examId, Integer number) {
-		List<AnswerCardVo> list=baseMapper.cardGet(examId, number);
-		if (CollectionUtils.isEmpty(list)) {
-			throw new ParameterException("未找到卡格式信息");
-		}
-		return list.get(0);
-	}
+        String filePath = null;
+        try {
+            fileService.uploadAdapteFile(file.getInputStream(), md5, examId, coursePaperId, cardNumber);
+        } catch (IOException e) {
+            throw new ParameterException("文件上传失败", e);
+        }
+        card.setAdapteMd5(md5);
+        card.setAdapteUri(filePath);
+        UriVo vo = new UriVo();
+        vo.setUri(filePath);
+        return vo;
+    }
+
+    @Override
+    public AnswerCardVo cardGet(Long examId, Integer number) {
+        List<AnswerCardVo> list = baseMapper.cardGet(examId, number);
+        if (CollectionUtils.isEmpty(list)) {
+            throw new ParameterException("未找到卡格式信息");
+        }
+        return list.get(0);
+    }
 
 }

+ 407 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/utils/FileStoreUtils.java

@@ -0,0 +1,407 @@
+package com.qmth.teachcloud.mark.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.qmth.boot.core.fss.service.FileService;
+import com.qmth.boot.core.fss.store.FileStore;
+import com.qmth.teachcloud.common.config.DictionaryConfig;
+import com.qmth.teachcloud.common.contant.SystemConstant;
+import com.qmth.teachcloud.common.domain.FssDomain;
+import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
+import com.qmth.teachcloud.common.enums.LocalCatalogEnum;
+import com.qmth.teachcloud.common.enums.UploadFileEnum;
+import com.qmth.teachcloud.mark.bean.FilePathVo;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.io.*;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.Objects;
+import java.util.StringJoiner;
+
+/**
+ * 文件存储工具类
+ */
+@Component
+public class FileStoreUtils {
+    private final static Logger log = LoggerFactory.getLogger(FileStoreUtils.class);
+
+    @Resource
+    private FileService fileService;
+
+    @Resource
+    private DictionaryConfig dictionaryConfig;
+
+    /**
+     * 上传文件到本地
+     *
+     * @param inputStream 流
+     * @param finalFile   最终文件
+     * @param catalogType 文件类型
+     * @throws Exception 异常
+     */
+    public void copyInputStreamToFile(InputStream inputStream, File finalFile, String md5, LocalCatalogEnum catalogType) throws Exception {
+        String dirName = finalFile.getPath().replaceAll("\\\\", SystemConstant.ORG_SPLIT);
+        this.localUpload(dirName, inputStream, md5, catalogType);
+    }
+
+    /**
+     * 上传文件到本地
+     *
+     * @param dirName     上传到地址
+     * @param inputStream 流
+     * @param catalogType 文件
+     */
+    public void localUpload(String dirName, InputStream inputStream, String md5, LocalCatalogEnum catalogType) throws Exception {
+        log.info("ossUpload is come in");
+        String configPath = "";
+        switch (catalogType) {
+            case LOCAL_FILE:
+                configPath = dictionaryConfig.fssPublicDomain().getConfig();
+                break;
+            case LOCAL_PDF:
+                configPath = dictionaryConfig.fssPrivateDomain().getConfig();
+                break;
+            default:
+                break;
+        }
+        dirName = dirName.replaceAll(configPath, "");
+        fileService.getFileStore(catalogType.getType()).write(dirName, inputStream, md5);
+        log.info("dirName:{}", dirName);
+    }
+
+    /**
+     * 上传文件到本地
+     *
+     * @param dirName     上传到地址
+     * @param inputStream 流
+     * @param fssType     文件
+     */
+    public void localUpload(String dirName, InputStream inputStream, String md5, String fssType) throws Exception {
+        log.info("ossUpload is come in");
+        FssDomain fssDomain = getFssDomain(fssType);
+        dirName = dirName.replaceAll(fssDomain.getConfig(), "");
+        fileService.getFileStore(fssType).write(dirName, inputStream, md5);
+        log.info("dirName:{}", dirName);
+    }
+
+    /**
+     * 上传文件
+     *
+     * @param dirName     上传到地址
+     * @param inputStream 流
+     * @param fssType     fileStore类型
+     */
+    public void ossUpload(String dirName, InputStream inputStream, String md5, String fssType) throws Exception {
+        log.info("ossUpload is come in");
+        fileService.getFileStore(fssType).write(dirName, inputStream, md5);
+        log.info("dirName:{}", dirName);
+    }
+
+
+    /**
+     * 上传文件
+     *
+     * @param dirName 上传到地址
+     * @param file    文件
+     * @param type    fileStore类型
+     */
+    public void ossUpload(String dirName, File file, String md5, String type) throws Exception {
+        log.info("ossUpload is come in");
+        fileService.getFileStore(type).write(dirName, new FileInputStream(file), md5);
+        log.info("dirName:{}", dirName);
+    }
+
+    /**
+     * 上传文件
+     *
+     * @param dirName 上传到地址
+     * @param content 文件
+     * @param type    fileStore类型
+     */
+    public void ossUpload(String dirName, String content, String type) throws Exception {
+        log.info("ossUpload is come in");
+        fileService.getFileStore(type).write(dirName, new ByteArrayInputStream(content.getBytes()), DigestUtils.md5Hex(new ByteArrayInputStream(content.getBytes())));
+        log.info("dirName:{}", dirName);
+    }
+
+    /**
+     * 从文件存储上下载文件到本地
+     *
+     * @param dirName   文件地址
+     * @param localPath 本地路径
+     * @param type      fileStore类型
+     * @throws Exception 异常
+     */
+    public File ossDownload(String dirName, String localPath, String type) throws Exception {
+        log.info("ossDownload is come in");
+        return this.saveLocal(fileService.getFileStore(type).read(dirName), localPath);
+    }
+
+    /**
+     * 从文件存储上下载文件到本地
+     *
+     * @param dirName   文件地址
+     * @param localFile 本地路径
+     * @param type      fileStore类型
+     * @throws Exception 异常
+     */
+    public File ossDownload(String dirName, File localFile, String type) throws Exception {
+        log.info("ossDownload is come in");
+        InputStream inputStream = fileService.getFileStore(type).read(dirName);
+        FileUtils.copyInputStreamToFile(inputStream, localFile);
+        return localFile;
+    }
+
+    /**
+     * 从文件存储上下载文件到byte[]
+     *
+     * @param objectName 文件地址
+     * @param type       fileStore类型
+     * @throws Exception 异常
+     */
+    public byte[] ossDownload(String objectName, String type) throws Exception {
+        log.info("oss Download is come in");
+        return IOUtils.toByteArray(fileService.getFileStore(type).read(objectName));
+    }
+
+    /**
+     * 从文件存储上下载文件到InputStream
+     *
+     * @param objectName 文件地址
+     * @param type       fileStore类型
+     * @throws Exception 异常
+     */
+    public InputStream ossDownloadIs(String objectName, String type) throws Exception {
+        log.info("oss Download is come in");
+        return fileService.getFileStore(type).read(objectName);
+    }
+
+    /**
+     * 获取文件访问url
+     *
+     * @param objectPath 文件路径
+     * @param fssType    文件上传的类型
+     * @return
+     */
+    public String getPrivateUrl(String objectPath, String fssType) {
+        String server;
+        FssDomain fssDomain = getFssDomain(fssType);
+        if ("public".equals(fssType)) {
+            server = fssDomain.getServer();
+            return server + SystemConstant.ORG_SPLIT + objectPath;
+        } else if ("private".equals(fssType)) {
+            Boolean oss = dictionaryConfig.sysDomain().isOss();
+            if (Objects.nonNull(oss) && oss) {
+                FileStore fileStore = fileService.getFileStore(fssType);
+                return fileStore.getPresignedUrl(objectPath, Duration.ofMinutes(5L));
+            } else {
+                return fssDomain.getServer() + SystemConstant.ORG_SPLIT + objectPath;
+            }
+        } else {
+            throw ExceptionResultEnum.ERROR.exception("文件存储store类型不存在");
+        }
+    }
+
+    /**
+     * 根据数据库文件路径判断文件上传类型
+     *
+     * @param path 路径
+     * @return 类型
+     */
+    public UploadFileEnum getUploadEnumByPath(String path) {
+        path = path.replaceAll("\\\\", SystemConstant.ORG_SPLIT);
+        String target = path.substring(0, path.indexOf('/'));
+        return UploadFileEnum.valueOf(target.toUpperCase());
+    }
+
+    /**
+     * 文件存在某本地路径
+     *
+     * @param inputStream 输入流
+     * @param dirPath     本地路径
+     * @return 存好的文件
+     * @throws IOException 异常
+     */
+    public File saveLocal(InputStream inputStream, String dirPath) throws IOException {
+        String fileName = dirPath.substring(dirPath.lastIndexOf(File.separator) + 1);
+        String parentPath = dirPath.substring(0, dirPath.lastIndexOf(File.separator)) + File.separator;
+        String name = fileName.substring(0, fileName.lastIndexOf('.'));
+        String suffix = fileName.substring(fileName.lastIndexOf('.'));
+
+        File desFile = buildSingleFileName(parentPath, name, suffix, 0);
+        if (!desFile.exists()) {
+            desFile.getParentFile().mkdirs(); //目标文件目录不存在的话需要创建目录
+            desFile.createNewFile();
+        }
+        try {
+            FileUtils.copyInputStreamToFile(inputStream, desFile);
+            return desFile;
+        } finally {
+            if (inputStream != null) {
+                inputStream.close();
+            }
+        }
+    }
+
+    /**
+     * 构建唯一的文件名称
+     *
+     * @param path     文件路径
+     * @param fileName 文件名称
+     * @param suffix   文件后缀
+     * @param index    当前下标,初次为0
+     * @return 唯一的文件名称
+     */
+    public static File buildSingleFileName(String path, String fileName, String suffix, Integer index) {
+        File file;
+        //下标不等于0开始拼后缀
+        if (index != 0) {
+            file = new File(path + fileName + "(" + index + ")" + suffix);
+        } else {
+            file = new File(path + fileName + suffix);
+        }
+        //判断文件是否存在 文件不存在退出递归
+        if (file.isFile()) {
+            //每次递归给下标加1
+            file = buildSingleFileName(path, fileName, suffix, ++index);
+        }
+        return file;
+    }
+
+    public String uploadFileByPattern(InputStream inputStream, String md5, UploadFileEnum uploadFileEnum, String path) {
+        try {
+            String fileMd5 = DigestUtils.md5Hex(inputStream);
+            if (!Objects.equals(fileMd5, md5)) {
+                throw ExceptionResultEnum.MD5_EQUALS_FALSE.exception();
+            }
+            FssDomain fssDomain = getFssDomain(uploadFileEnum.getFssType());
+            boolean oss = dictionaryConfig.sysDomain().isOss();
+            String fileName = getFssPathByPattern(oss, uploadFileEnum, path);
+            String type;
+            //上传至oss
+            if (oss || fssDomain.getConfig().startsWith(SystemConstant.START_PARENT)) {
+                this.ossUpload(fileName, inputStream, md5, uploadFileEnum.getFssType());
+                type = SystemConstant.OSS;
+            }
+            // 上传本地服务器
+            else {
+                this.localUpload(fileName, inputStream, md5, uploadFileEnum.getFssType());
+                type = SystemConstant.LOCAL;
+            }
+            return JSON.toJSONString(new FilePathVo(fileName, uploadFileEnum, type));
+        } catch (Exception e) {
+            throw ExceptionResultEnum.ERROR.exception("文件上传失败");
+        }
+    }
+
+    public String filePreview(String path) {
+        String url = null;
+        FilePathVo filePathVo = JSON.parseObject(path, FilePathVo.class);
+        String type = filePathVo.getType();
+        String filePath = filePathVo.getPath();
+        UploadFileEnum uploadFileEnum = UploadFileEnum.valueOf(filePathVo.getUploadType());
+        FssDomain fssDomain = getFssDomain(uploadFileEnum.getFssType());
+        if (Objects.equals(type, SystemConstant.LOCAL)) {
+            if (filePath.contains(fssDomain.getServer())) {
+                url = filePath.substring(filePath.indexOf(fssDomain.getServer()), filePath.length());
+            } else {
+                url = getPrivateUrl(filePath, uploadFileEnum.getFssType());
+            }
+        } else {
+            url = getPrivateUrl(filePath, uploadFileEnum.getFssType());
+        }
+        url = url.replaceAll("\\\\", SystemConstant.ORG_SPLIT);
+        return url;
+    }
+
+    /**
+     * 文件上传存储类型(私有、公有)
+     *
+     * @param fssType 文件上传类型
+     */
+    private FssDomain getFssDomain(String fssType) {
+        if (StringUtils.isBlank(fssType)) {
+            throw ExceptionResultEnum.ERROR.exception("文件类型异常");
+        }
+        String type = fssType.toLowerCase();
+        FssDomain fssDomain = null;
+        switch (type) {
+            case "private":
+                fssDomain = dictionaryConfig.fssPrivateDomain();
+                break;
+            case "public":
+                fssDomain = dictionaryConfig.fssPublicDomain();
+                break;
+            default:
+                break;
+        }
+        if (fssDomain == null) {
+            throw ExceptionResultEnum.ERROR.exception("文件上传类型异常");
+        }
+        return fssDomain;
+    }
+
+    /**
+     * 使用模板生成文件名
+     *
+     * @param oss            是否上传oss
+     * @param uploadFileEnum 文件上传类型
+     * @param path           文件路径
+     */
+    private String getFssPathByPattern(boolean oss, UploadFileEnum uploadFileEnum, String path) {
+        FssDomain fssDomain = getFssDomain(uploadFileEnum.getFssType());
+        StringJoiner stringJoiner = new StringJoiner("");
+        if (!oss && StringUtils.isNotBlank(fssDomain.getConfig()) && !fssDomain.getConfig().startsWith(SystemConstant.START_PARENT)) {
+            stringJoiner.add(fssDomain.getConfig()).add(File.separator);
+        }
+        stringJoiner.add(path);
+        return escapeFileName(stringJoiner.toString());
+    }
+
+    /**
+     * 使用日期创建目录
+     *
+     * @param oss            是否上传oss
+     * @param uploadFileEnum 文件上传类型
+     * @param fileName       原文件名称
+     * @param useRandomName  是否使用文件别名
+     */
+    public String getFssPathByDate(boolean oss, UploadFileEnum uploadFileEnum, String fileName, boolean useRandomName) {
+        FssDomain fssDomain = getFssDomain(uploadFileEnum.getFssType());
+        StringJoiner stringJoiner = new StringJoiner("");
+        if (!oss && StringUtils.isNotBlank(fssDomain.getConfig()) && !fssDomain.getConfig().startsWith(SystemConstant.START_PARENT)) {
+            stringJoiner.add(fssDomain.getConfig()).add(File.separator);
+        }
+        LocalDateTime nowTime = LocalDateTime.now();
+        stringJoiner.add(uploadFileEnum.getTitle()).add(File.separator)
+                .add(String.valueOf(nowTime.getYear())).add(File.separator)
+                .add(String.format(SystemConstant.DATE_TIME_FORMAT, nowTime.getMonthValue())).add(File.separator)
+                .add(String.format(SystemConstant.DATE_TIME_FORMAT, nowTime.getDayOfMonth())).add(File.separator);
+        if (useRandomName) {
+            stringJoiner.add(SystemConstant.getNanoId()).add(FilenameUtils.getExtension(fileName));
+        } else {
+            stringJoiner.add(fileName);
+        }
+        return escapeFileName(stringJoiner.toString());
+    }
+
+    /**
+     * 处理路径中"\\"
+     *
+     * @param fileName 文件名称
+     */
+    private String escapeFileName(String fileName) {
+        if (StringUtils.isBlank(fileName)) {
+            return null;
+        }
+        return fileName.replaceAll("\\\\", SystemConstant.ORG_SPLIT);
+    }
+}