OssFileService.java 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. package edu.nju.service;
  2. import edu.nju.entities.BugDetail;
  3. import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  4. import org.springframework.web.multipart.MultipartFile;
  5. import java.util.List;
  6. import com.alibaba.fastjson.JSON;
  7. import com.aliyun.oss.OSS;
  8. import edu.nju.dao.*;
  9. import edu.nju.entities.*;
  10. import edu.nju.util.OssAliyun;
  11. import edu.nju.util.TransUtil;
  12. import org.json.JSONArray;
  13. import org.springframework.beans.factory.annotation.Autowired;
  14. import org.springframework.beans.factory.annotation.Value;
  15. import org.springframework.stereotype.Service;
  16. import java.io.*;
  17. import java.lang.reflect.Field;
  18. import java.nio.charset.StandardCharsets;
  19. import java.util.*;
  20. import java.util.zip.ZipEntry;
  21. import java.util.zip.ZipFile;
  22. import java.util.zip.ZipOutputStream;
  23. /**
  24. * @Author JiaWei Xu
  25. * @Date 2021-01-04 15:53
  26. * @Email xjwhhh233@outlook.com
  27. */
  28. @Service
  29. @ConditionalOnExpression("${useOss}==true")
  30. public class OssFileService implements FileService {
  31. private static final int BUFFER_SIZE = 2048;
  32. @Autowired
  33. ExamDao examDao;
  34. @Autowired
  35. BugDao bugDao;
  36. @Autowired
  37. ReportDao reportDao;
  38. @Autowired
  39. TestCaseDao testCaseDao;
  40. @Autowired
  41. CaseToBugDao caseToBugDao;
  42. @Autowired
  43. BugScoreDao bugScoreDao;
  44. @Autowired
  45. BugMirrorDao bugMirrorDao;
  46. @Autowired
  47. BugHistoryDao bugHistoryDao;
  48. @Autowired
  49. BugDetailDao bugDetailDao;
  50. @Value("${cpSerialNum}")
  51. private String cpSerialNum;
  52. @Value("${oss.bucketName}")
  53. private String bucketName;
  54. @Value("${oss.imageUrlPrefix}")
  55. private String ossImageUrlPrefix;
  56. String localPrefix ="";
  57. @Override
  58. public void uploadImage() {
  59. }
  60. @Override
  61. public List<BugDetail> importBugInfo(MultipartFile sourceZipFile, MultipartFile sourceJsonFile, String originalCaseId, String cpSerialNum) {
  62. return saveBugDetail(sourceZipFile,sourceJsonFile,originalCaseId,cpSerialNum);
  63. }
  64. @Override
  65. public List<BugDetail> exportBugInfo(String caseId) {
  66. List<BugDetail> bugDetailList = getBugDetailListByCaseId(caseId);
  67. bugDetailToFile(bugDetailList,caseId);
  68. return bugDetailList;
  69. }
  70. /**
  71. * 根据caseId获取对应bug信息
  72. *
  73. * @param caseId
  74. * @return
  75. */
  76. public List<BugDetail> getBugDetailListByCaseId(String caseId) {
  77. List<BugDetail> bugDetailList = new ArrayList<>();
  78. Exam crowdCase = examDao.findById(caseId);
  79. if (crowdCase != null) {
  80. List<Report> reportList = reportDao.findByCaseId(caseId);
  81. for (Report report : reportList) {
  82. String reportId = report.getId();
  83. List<TestCase> testCaseList = testCaseDao.findByReport(reportId);
  84. for (TestCase testCase : testCaseList) {
  85. String testCaseId = testCase.getId();
  86. CaseToBug caseToBug = caseToBugDao.findById(testCaseId);
  87. if (caseToBug != null) {
  88. List<String> bugIdList = caseToBug.getBug_id();
  89. for (String bugId : bugIdList) {
  90. BugDetail bugDetail = new BugDetail();
  91. bugDetail.setId(bugId);
  92. //bug基本属性
  93. Bug bug = bugDao.findByid(bugId);
  94. if (bug != null) {
  95. bugDetail.setBugCategory(bug.getBug_category());
  96. bugDetail.setSeverity(TransUtil.severityTransFromInt(bug.getSeverity()));
  97. bugDetail.setRecurrent(TransUtil.recurrentTransFromInt(bug.getRecurrent()));
  98. bugDetail.setBugCreateTime(TransUtil.formatTimeMillis(bug.getCreate_time_millis()));
  99. bugDetail.setBugPage(bug.getBug_page());
  100. bugDetail.setTitle(bug.getTitle());
  101. bugDetail.setBugDescription(bug.getDescription());
  102. bugDetail.setImgUrl(bug.getImg_url());
  103. }
  104. //bugScore属性
  105. BugScore bugScore = bugScoreDao.findById(bugId);
  106. if (bugScore != null) {
  107. bugDetail.setScore(bugScore.getGrade());
  108. }
  109. //bugMirror属性
  110. BugMirror bugMirror = bugMirrorDao.findById(bugId);
  111. if (bugMirror != null) {
  112. Set<String> goodWorkerIdSet = new HashSet<>();
  113. Set<String> badWorkerIdSet = new HashSet<>();
  114. Set<String> goodReportIdSet = bugMirror.getGood();
  115. Set<String> badReportIdSet = bugMirror.getBad();
  116. int goodNum = 0;
  117. int badNum = 0;
  118. for (String goodReportId : goodReportIdSet) {
  119. Report goodReport = reportDao.findById(goodReportId);
  120. if (goodReport != null) {
  121. goodNum++;
  122. goodWorkerIdSet.add(goodReport.getWorker_id());
  123. }
  124. }
  125. for (String badReportId : badReportIdSet) {
  126. Report badReport = reportDao.findById(badReportId);
  127. if (badReport != null) {
  128. badNum++;
  129. badWorkerIdSet.add(badReport.getWorker_id());
  130. }
  131. }
  132. bugDetail.setGoodNum(goodNum);
  133. bugDetail.setBadNum(badNum);
  134. bugDetail.setGoodWorkerId(goodWorkerIdSet);
  135. bugDetail.setBadWorkerId(badWorkerIdSet);
  136. }
  137. //bugHistory属性
  138. BugHistory bugHistory = bugHistoryDao.findByid(bugId);
  139. if (bugHistory != null) {
  140. bugDetail.setParent(bugHistory.getParent());
  141. bugDetail.setChildren(bugHistory.getChildren());
  142. bugDetail.setRoot(bugHistory.getRoot());
  143. }
  144. //testCase属性
  145. bugDetail.setTestCaseId(testCase.getId());
  146. bugDetail.setTestCaseName(testCase.getName());
  147. bugDetail.setTestCaseFront(testCase.getFront());
  148. bugDetail.setTestCaseBehind(testCase.getBehind());
  149. bugDetail.setTestCaseDescription(testCase.getDescription());
  150. bugDetail.setTestCaseCreateTime(TransUtil.formatTimeMillis(testCase.getCreate_time_millis()));
  151. //report属性
  152. bugDetail.setReportId(report.getId());
  153. bugDetail.setReportName(report.getName());
  154. bugDetail.setScriptLocation(report.getScript_location());
  155. bugDetail.setReportLocation(report.getReport_location());
  156. bugDetail.setLogLocation(report.getLog_location());
  157. bugDetail.setDeviceModel(report.getDevice_model());
  158. bugDetail.setDeviceBrand(report.getDevice_brand());
  159. bugDetail.setDeviceOs(report.getDevice_os());
  160. //worker属性
  161. bugDetail.setWorkerId(report.getWorker_id());
  162. //众测任务属性
  163. bugDetail.setCaseAppName(crowdCase.getName());
  164. bugDetail.setCasePaperType(crowdCase.getPaper_type());
  165. bugDetail.setCaseTestType(crowdCase.getTest_type());
  166. bugDetail.setCaseDescription(crowdCase.getDescription());
  167. bugDetail.setCaseRequireDoc("");
  168. bugDetail.setCaseTakeId(report.getCase_take_id());
  169. //cp序列号
  170. bugDetail.setCpSerialNum(cpSerialNum);
  171. bugDetailList.add(bugDetail);
  172. }
  173. }
  174. }
  175. }
  176. }
  177. return bugDetailList;
  178. }
  179. public List<BugDetail> saveBugDetail(MultipartFile sourceZipFile, MultipartFile sourceJsonFile, String originalCaseId, String cpSerialNum) {
  180. try {
  181. //读取文件流并保存在本地
  182. String zipFilePath= localPrefix +"/xinchuangdata/input/imageZip/"+originalCaseId+"/"+cpSerialNum+"/"+originalCaseId+".zip";
  183. File zipFile=new File(zipFilePath);
  184. if(!zipFile.getParentFile().exists()) { zipFile.getParentFile().mkdirs(); }
  185. if(!sourceZipFile.isEmpty()) { sourceZipFile.transferTo(zipFile); }
  186. String jsonFilePath= localPrefix +"/xinchuangdata/input/"+originalCaseId+"/"+cpSerialNum+"/"+originalCaseId+".json";
  187. File jsonFile=new File(jsonFilePath);
  188. if(!jsonFile.getParentFile().exists()) { jsonFile.getParentFile().mkdirs(); }
  189. if(!sourceJsonFile.isEmpty()) { sourceJsonFile.transferTo(jsonFile); }
  190. //读取本地文件
  191. BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(jsonFile));
  192. ByteArrayOutputStream buf = new ByteArrayOutputStream();
  193. int result = bufferedInputStream.read();
  194. while (result != -1) {
  195. buf.write((byte) result);
  196. result = bufferedInputStream.read();
  197. }
  198. String json = buf.toString();
  199. //转为bugDetail
  200. List<BugDetail> bugDetailList = JSON.parseArray(json, BugDetail.class);
  201. for (BugDetail bugDetail : bugDetailList) {
  202. bugDetail.setOriginalCaseId(originalCaseId);
  203. //修改图片文件路径为oss路径
  204. String imageUrl = bugDetail.getImgUrl();
  205. String[] imageUrlArray = imageUrl.split(",");
  206. StringBuilder stringBuilder = new StringBuilder();
  207. for (String imageUrlStr : imageUrlArray) {
  208. if(!"".equals(imageUrl)) {
  209. String[] filePath = imageUrlStr.split("/");
  210. String fileName = filePath[filePath.length - 1];
  211. String newImageUrl = ossImageUrlPrefix + originalCaseId + "/" + cpSerialNum + "/" + fileName;
  212. stringBuilder.append(newImageUrl).append(",");
  213. }
  214. }
  215. bugDetail.setImgUrl(stringBuilder.toString());
  216. bugDetailDao.save(bugDetail);
  217. }
  218. //解压图片文件,上传至oss
  219. String destPath= localPrefix +"/xinchuangdata/input/imageUnzip/"+originalCaseId+"/"+cpSerialNum;
  220. File unzipFile=new File(destPath);
  221. if(!unzipFile.getParentFile().exists()) { unzipFile.getParentFile().mkdirs(); }
  222. unZip(zipFile,destPath,originalCaseId,cpSerialNum);
  223. return bugDetailList;
  224. } catch (IOException e) {
  225. e.printStackTrace();
  226. }
  227. return new ArrayList<>();
  228. }
  229. private void bugDetailToFile(List<BugDetail> bugDetailList, String caseId) {
  230. String[] titles = {"bug_id", "bug_category", "severity", "recurrent", "bug_create_time", "bug_page", "title",
  231. "description", "img_url",
  232. "score", "parent", "children", "root", "good_num", "good_worker_id", "bad_num", "bad_worker_id",
  233. "test_case_id", "test_case_name", "test_case_front", "test_case_behind", "test_case_description", "test_case_create_time",
  234. "report_id", "report_name", "report_create_time", "script_location", "report_location", "log_location", "device_model", "device_brand", "device_os",
  235. "worker_id",
  236. "case_app_name", "case_paper_type", "case_test_type", "case_description", "case_require_doc",
  237. "case_take_id", "originalCaseId", "cpSerialNum"};
  238. File csvFile = exportCsv(titles, bugDetailList, caseId);
  239. File jsonFile = exportJson(bugDetailList, caseId);
  240. String csvObjectName = "xinchuang/output/"+caseId+"/"+cpSerialNum+"/" + csvFile.getName();
  241. String jsonObjectName = "xinchuang/output/"+caseId+"/"+cpSerialNum+"/" + jsonFile.getName();
  242. uploadToOss(csvObjectName,csvFile);
  243. uploadToOss(jsonObjectName,jsonFile);
  244. }
  245. private File exportJson(List<BugDetail> bugDetailList, String caseId) {
  246. try {
  247. File file = new File(localPrefix+"/xinchuangdata/output/" + caseId + ".json");
  248. if(!file.getParentFile().exists()) { file.getParentFile().mkdirs(); }
  249. JSONArray jsonArray = new JSONArray(bugDetailList);
  250. Writer write = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
  251. write.write(jsonArray.toString());
  252. write.flush();
  253. write.close();
  254. return file;
  255. } catch (IOException e) {
  256. e.printStackTrace();
  257. }
  258. return null;
  259. }
  260. private <T> File exportCsv(String[] titles, List<T> list, String caseId) {
  261. try {
  262. File file = new File(localPrefix+"/xinchuangdata/output/" + caseId + ".csv");
  263. if(!file.getParentFile().exists()) { file.getParentFile().mkdirs(); }
  264. //构建输出流,同时指定编码
  265. OutputStreamWriter ow = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
  266. //csv文件是逗号分隔,除第一个外,每次写入一个单元格数据后需要输入逗号
  267. for (String title : titles) {
  268. ow.write(title);
  269. ow.write(",");
  270. }
  271. //写完文件头后换行
  272. ow.write("\r\n");
  273. //写内容
  274. for (Object obj : list) {
  275. //利用反射获取所有字段
  276. Field[] fields = obj.getClass().getDeclaredFields();
  277. for (Field field : fields) {
  278. //设置字段可见性
  279. field.setAccessible(true);
  280. //防止某个field没有赋值
  281. if (field.get(obj) == null) {
  282. ow.write("");
  283. } else {
  284. //解决csv文件中对于逗号和双引号的转义问题
  285. ow.write("\"" + field.get(obj).toString().replaceAll("\"", "\"\"") + "\"");
  286. }
  287. ow.write(",");
  288. }
  289. //写完一行换行
  290. ow.write("\r\n");
  291. }
  292. ow.flush();
  293. ow.close();
  294. return file;
  295. } catch (IOException | IllegalAccessException e) {
  296. e.printStackTrace();
  297. }
  298. return null;
  299. }
  300. private void uploadToOss(String objectName,File file) {
  301. if (file != null) {
  302. OSS ossClient = OssAliyun.initShangHaiOss();
  303. OssAliyun.uploadFile(ossClient, bucketName, objectName, file);
  304. } else {
  305. System.out.println("file is null");
  306. }
  307. }
  308. /**
  309. * zip解压
  310. *
  311. * @param srcFile zip源文件
  312. * @param destDirPath 解压后的目标文件夹
  313. * @throws RuntimeException 解压失败会抛出运行时异常
  314. */
  315. private void unZip(File srcFile, String destDirPath,String originalCaseId,String fromCpSerialNum) throws RuntimeException {
  316. long start = System.currentTimeMillis();
  317. // 判断源文件是否存在
  318. if (!srcFile.exists()) {
  319. throw new RuntimeException(srcFile.getPath() + "所指文件不存在");
  320. }
  321. // 开始解压
  322. ZipFile zipFile = null;
  323. try {
  324. zipFile = new ZipFile(srcFile);
  325. Enumeration<?> entries = zipFile.entries();
  326. while (entries.hasMoreElements()) {
  327. ZipEntry entry = (ZipEntry) entries.nextElement();
  328. System.out.println("解压" + entry.getName());
  329. // 如果是文件夹,就创建个文件夹
  330. if (entry.isDirectory()) {
  331. String dirPath = destDirPath + "/" + entry.getName();
  332. File dir = new File(dirPath);
  333. dir.mkdirs();
  334. } else {
  335. // 如果是文件,就先创建一个文件,然后用io流把内容copy过去
  336. File targetFile = new File(destDirPath + "/" + entry.getName());
  337. // 保证这个文件的父文件夹必须要存在
  338. if (!targetFile.getParentFile().exists()) {
  339. targetFile.getParentFile().mkdirs();
  340. }
  341. targetFile.createNewFile();
  342. // 将压缩文件内容写入到这个文件中
  343. InputStream is = zipFile.getInputStream(entry);
  344. FileOutputStream fos = new FileOutputStream(targetFile);
  345. int len;
  346. byte[] buf = new byte[BUFFER_SIZE];
  347. while ((len = is.read(buf)) != -1) {
  348. fos.write(buf, 0, len);
  349. }
  350. // 关流顺序,先打开的后关闭
  351. fos.close();
  352. is.close();
  353. //图片文件上传至oss
  354. String objectName = "xinchuang/image/"+originalCaseId+"/"+fromCpSerialNum+"/" + targetFile.getName();
  355. uploadToOss(objectName,targetFile);
  356. }
  357. }
  358. long end = System.currentTimeMillis();
  359. System.out.println("解压完成,耗时:" + (end - start) + " ms");
  360. } catch (Exception e) {
  361. throw new RuntimeException("unzip error from ZipUtils", e);
  362. } finally {
  363. if (zipFile != null) {
  364. try {
  365. zipFile.close();
  366. } catch (IOException e) {
  367. e.printStackTrace();
  368. }
  369. }
  370. }
  371. }
  372. private void toZip(String srcDir, OutputStream out, boolean keepDirStructure)
  373. throws RuntimeException {
  374. long start = System.currentTimeMillis();
  375. ZipOutputStream zos = null;
  376. try {
  377. zos = new ZipOutputStream(out);
  378. File sourceFile = new File(srcDir);
  379. compress(sourceFile, zos, sourceFile.getName(), keepDirStructure);
  380. long end = System.currentTimeMillis();
  381. System.out.println("压缩完成,耗时:" + (end - start) + " ms");
  382. } catch (Exception e) {
  383. throw new RuntimeException("zip error from ZipUtils", e);
  384. } finally {
  385. if (zos != null) {
  386. try {
  387. zos.close();
  388. } catch (IOException e) {
  389. e.printStackTrace();
  390. }
  391. }
  392. }
  393. }
  394. /**
  395. * 递归压缩方法
  396. *
  397. * @param sourceFile 源文件
  398. * @param zos zip输出流
  399. * @param name 压缩后的名称
  400. * @param keepDirStructure 是否保留原来的目录结构,true:保留目录结构;
  401. * false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
  402. * @throws Exception
  403. */
  404. private void compress(File sourceFile, ZipOutputStream zos, String name,
  405. boolean keepDirStructure) throws Exception {
  406. byte[] buf = new byte[BUFFER_SIZE];
  407. if (sourceFile.isFile()) {
  408. // 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字
  409. zos.putNextEntry(new ZipEntry(name));
  410. // copy文件到zip输出流中
  411. int len;
  412. FileInputStream in = new FileInputStream(sourceFile);
  413. while ((len = in.read(buf)) != -1) {
  414. zos.write(buf, 0, len);
  415. }
  416. // Complete the entry
  417. zos.closeEntry();
  418. in.close();
  419. } else {
  420. File[] listFiles = sourceFile.listFiles();
  421. if (listFiles == null || listFiles.length == 0) {
  422. // 需要保留原来的文件结构时,需要对空文件夹进行处理
  423. if (keepDirStructure) {
  424. // 空文件夹的处理
  425. zos.putNextEntry(new ZipEntry(name + "/"));
  426. // 没有文件,不需要文件的copy
  427. zos.closeEntry();
  428. }
  429. } else {
  430. for (File file : listFiles) {
  431. // 判断是否需要保留原来的文件结构
  432. if (keepDirStructure) {
  433. // 注意:file.getName()前面需要带上父文件夹的名字加一斜杠,
  434. // 不然最后压缩包中就不能保留原来的文件结构,即:所有文件都跑到压缩包根目录下了
  435. compress(file, zos, name + "/" + file.getName(), true);
  436. } else {
  437. compress(file, zos, file.getName(), false);
  438. }
  439. }
  440. }
  441. }
  442. }
  443. }