NginxFileService.java 22 KB

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