NginxFileService.java 21 KB

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