diff --git a/build.bat b/build.bat index f0b4c1c7..aa404725 100644 --- a/build.bat +++ b/build.bat @@ -1 +1,2 @@ -mvn clean package \ No newline at end of file +mvn clean package +PAUSE \ No newline at end of file diff --git a/clean.bat b/clean.bat index 2f3d3e51..0644e46d 100644 --- a/clean.bat +++ b/clean.bat @@ -1 +1,2 @@ -mvn clean \ No newline at end of file +mvn clean +PAUSE \ No newline at end of file diff --git a/jeecg-module-main/pom.xml b/jeecg-module-main/pom.xml index 9e9338b3..1d7d6b9e 100644 --- a/jeecg-module-main/pom.xml +++ b/jeecg-module-main/pom.xml @@ -61,8 +61,42 @@ logging-interceptor 2.7.5 + + + + + com.artofsolving + jodconverter + 2.2.1 + + + org.openoffice + bootstrap-connector + 0.1.1 + + + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.1.0 + + + woff + woff2 + eot + ttf + svg + docx + + + + + \ No newline at end of file diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/controller/KcExportConfigTpkwcqkjzglxController.java b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/controller/KcExportConfigTpkwcqkjzglxController.java new file mode 100644 index 00000000..087f359d --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/controller/KcExportConfigTpkwcqkjzglxController.java @@ -0,0 +1,201 @@ +package org.jeecg.modules.kc.config.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.aspect.annotation.AutoLog; +import org.jeecg.common.system.base.controller.JeecgController; +import org.jeecg.common.system.query.QueryGenerator; +import org.jeecg.modules.kc.config.entity.KcExportConfigTpkwcqkjzglx; +import org.jeecg.modules.kc.config.export.Export; +import org.jeecg.modules.kc.config.service.IKcExportConfigTpkwcqkjzglxService; +import org.jeecg.modules.kc.grab.imports.service.IXxhbuserService; +import org.jeecg.modules.tools.word.ExportWord; +import org.jeecg.modules.tools.word.WordOperator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Arrays; + + /** + * @Description: kc_export_config_tpkwcqkjzglx + * @Author: jeecg-boot + * @Date: 2023-07-21 + * @Version: V1.0 + */ +@Api(tags="kc_export_config_tpkwcqkjzglx") +@RestController +@RequestMapping("/config/kcExportConfigTpkwcqkjzglx") +@Slf4j +public class KcExportConfigTpkwcqkjzglxController extends JeecgController { + @Autowired + private IKcExportConfigTpkwcqkjzglxService kcExportConfigTpkwcqkjzglxService; + + @Autowired + private IXxhbuserService xxhbuserService; + + /** + * 分页列表查询 + * + * @param kcExportConfigTpkwcqkjzglx + * @param pageNo + * @param pageSize + * @param req + * @return + */ + //@AutoLog(value = "kc_export_config_tpkwcqkjzglx-分页列表查询") + @ApiOperation(value="kc_export_config_tpkwcqkjzglx-分页列表查询", notes="kc_export_config_tpkwcqkjzglx-分页列表查询") + @GetMapping(value = "/list") + public Result> queryPageList(KcExportConfigTpkwcqkjzglx kcExportConfigTpkwcqkjzglx, + @RequestParam(name="pageNo", defaultValue="1") Integer pageNo, + @RequestParam(name="pageSize", defaultValue="10") Integer pageSize, + HttpServletRequest req) { + QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper("a", kcExportConfigTpkwcqkjzglx, req.getParameterMap()); + queryWrapper.eq(StringUtils.isNotBlank(kcExportConfigTpkwcqkjzglx.getDwmc()),"b.dwmc",kcExportConfigTpkwcqkjzglx.getDwmc()); + Page page = new Page(pageNo, pageSize); + IPage pageList = kcExportConfigTpkwcqkjzglxService.page(page, queryWrapper); + return Result.OK(pageList); + } + + /** + * 添加 + * + * @param kcExportConfigTpkwcqkjzglx + * @return + */ + @AutoLog(value = "kc_export_config_tpkwcqkjzglx-添加") + @ApiOperation(value="kc_export_config_tpkwcqkjzglx-添加", notes="kc_export_config_tpkwcqkjzglx-添加") +// @RequiresPermissions("config:kc_export_config_tpkwcqkjzglx:add") + @PostMapping(value = "/add") + public Result add(@RequestBody KcExportConfigTpkwcqkjzglx kcExportConfigTpkwcqkjzglx) { + kcExportConfigTpkwcqkjzglxService.save(kcExportConfigTpkwcqkjzglx); + return Result.OK("添加成功!"); + } + + /** + * 编辑 + * + * @param kcExportConfigTpkwcqkjzglx + * @return + */ + @AutoLog(value = "kc_export_config_tpkwcqkjzglx-编辑") + @ApiOperation(value="kc_export_config_tpkwcqkjzglx-编辑", notes="kc_export_config_tpkwcqkjzglx-编辑") +// @RequiresPermissions("config:kc_export_config_tpkwcqkjzglx:edit") + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST}) + public Result edit(@RequestBody KcExportConfigTpkwcqkjzglx kcExportConfigTpkwcqkjzglx) { + kcExportConfigTpkwcqkjzglxService.updateById(kcExportConfigTpkwcqkjzglx); + return Result.OK("编辑成功!"); + } + + /** + * 通过id删除 + * + * @param id + * @return + */ + @AutoLog(value = "kc_export_config_tpkwcqkjzglx-通过id删除") + @ApiOperation(value="kc_export_config_tpkwcqkjzglx-通过id删除", notes="kc_export_config_tpkwcqkjzglx-通过id删除") +// @RequiresPermissions("config:kc_export_config_tpkwcqkjzglx:delete") + @DeleteMapping(value = "/delete") + public Result delete(@RequestParam(name="id",required=true) String id) { + kcExportConfigTpkwcqkjzglxService.removeById(id); + return Result.OK("删除成功!"); + } + + /** + * 批量删除 + * + * @param ids + * @return + */ + @AutoLog(value = "kc_export_config_tpkwcqkjzglx-批量删除") + @ApiOperation(value="kc_export_config_tpkwcqkjzglx-批量删除", notes="kc_export_config_tpkwcqkjzglx-批量删除") +// @RequiresPermissions("config:kc_export_config_tpkwcqkjzglx:deleteBatch") + @DeleteMapping(value = "/deleteBatch") + public Result deleteBatch(@RequestParam(name="ids",required=true) String ids) { + this.kcExportConfigTpkwcqkjzglxService.removeByIds(Arrays.asList(ids.split(","))); + return Result.OK("批量删除成功!"); + } + + /** + * 通过id查询 + * + * @param id + * @return + */ + //@AutoLog(value = "kc_export_config_tpkwcqkjzglx-通过id查询") + @ApiOperation(value="kc_export_config_tpkwcqkjzglx-通过id查询", notes="kc_export_config_tpkwcqkjzglx-通过id查询") + @GetMapping(value = "/queryById") + public Result queryById(@RequestParam(name="id",required=true) String id) { + KcExportConfigTpkwcqkjzglx kcExportConfigTpkwcqkjzglx = kcExportConfigTpkwcqkjzglxService.getById(id); + if(kcExportConfigTpkwcqkjzglx==null) { + return Result.error("未找到对应数据"); + } + return Result.OK(kcExportConfigTpkwcqkjzglx); + } + + /** + * 导出excel + * + * @param request + * @param kcExportConfigTpkwcqkjzglx + */ + @RequiresPermissions("config:kc_export_config_tpkwcqkjzglx:exportXls") + @RequestMapping(value = "/exportXls") + public ModelAndView exportXls(HttpServletRequest request, KcExportConfigTpkwcqkjzglx kcExportConfigTpkwcqkjzglx) { + return super.exportXls(request, kcExportConfigTpkwcqkjzglx, KcExportConfigTpkwcqkjzglx.class, "kc_export_config_tpkwcqkjzglx"); + } + + /** + * 通过excel导入数据 + * + * @param request + * @param response + * @return + */ + @RequiresPermissions("config:kc_export_config_tpkwcqkjzglx:importExcel") + @RequestMapping(value = "/importExcel", method = RequestMethod.POST) + public Result importExcel(HttpServletRequest request, HttpServletResponse response) { + return super.importExcel(request, response, KcExportConfigTpkwcqkjzglx.class); + } + + /** + * 导出word + * + * @param xqxnParam + * @param request + * @param response + */ +// @RequiresPermissions("config:kc_export_config_tpkwcqkjzglx:exportXls") + @RequestMapping(value = "/exportWord") + public void exportWord(KcExportConfigTpkwcqkjzglx kcExportConfigTpkwcqkjzglx, String xqxnParam, HttpServletRequest request, HttpServletResponse response) throws Exception { + Export export = new Export(); + WordOperator wo = export.export(kcExportConfigTpkwcqkjzglx, xqxnParam); + ExportWord.download_word(request, response, "file.doc", wo); + } + + /** + * 导出word + * + * @param xqxnParam + * @param request + * @param response + */ +// @RequiresPermissions("config:kc_export_config_tpkwcqkjzglx:exportXls") + @RequestMapping(value = "/exportPdf") + public void exportPdf(KcExportConfigTpkwcqkjzglx kcExportConfigTpkwcqkjzglx, String xqxnParam, HttpServletRequest request, HttpServletResponse response) throws Exception { + Export export = new Export(); + WordOperator wo = export.export(kcExportConfigTpkwcqkjzglx, xqxnParam); + ExportWord.download_pdf(request, response, "file", wo); + } + +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/entity/KcExportConfigTpkwcqkjzglx.java b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/entity/KcExportConfigTpkwcqkjzglx.java new file mode 100644 index 00000000..b35d481b --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/entity/KcExportConfigTpkwcqkjzglx.java @@ -0,0 +1,75 @@ +package org.jeecg.modules.kc.config.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecg.common.aspect.annotation.Dict; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; + +/** + * @Description: kc_export_config_tpkwcqkjzglx + * @Author: jeecg-boot + * @Date: 2023-07-21 + * @Version: V1.0 + */ +@Data +@TableName("kc_export_config_tpkwcqkjzglx") +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@ApiModel(value="kc_export_config_tpkwcqkjzglx对象", description="kc_export_config_tpkwcqkjzglx") +public class KcExportConfigTpkwcqkjzglx implements Serializable { + private static final long serialVersionUID = 1L; + + /**id*/ + @TableId(type = IdType.ASSIGN_ID) + @ApiModelProperty(value = "id") + private java.lang.String id; + /**创建人*/ + @ApiModelProperty(value = "创建人") + private java.lang.String createBy; + /**创建日期*/ + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern="yyyy-MM-dd") + @ApiModelProperty(value = "创建日期") + private java.util.Date createTime; + /**更新人*/ + @ApiModelProperty(value = "更新人") + private java.lang.String updateBy; + /**更新日期*/ + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern="yyyy-MM-dd") + @ApiModelProperty(value = "更新日期") + private java.util.Date updateTime; + /**所属部门*/ + @ApiModelProperty(value = "所属部门") + private java.lang.String sysOrgCode; + /**工号*/ + @Excel(name = "工号", width = 15, dictTable = "xxhbuser", dicText = "xm", dicCode = "gh") + @Dict(dictTable = "xxhbuser", dicText = "xm", dicCode = "gh") + @ApiModelProperty(value = "工号") + private java.lang.String gh; + /**听课类型(字典:tpkwcqkjzglx )*/ + @Excel(name = "听课类型(字典:tpkwcqkjzglx )", width = 15, dicCode = "tpkwcqkjzglx") + @Dict(dicCode = "tpkwcqkjzglx") + @ApiModelProperty(value = "听课类型(字典:tpkwcqkjzglx )") + private java.lang.String tklx; + + //教职工姓名 + @TableField(exist = false) + private java.lang.String xm; + + //单位名称 + @TableField(exist = false) + private java.lang.String dwmc; + +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/export/Export.java b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/export/Export.java new file mode 100644 index 00000000..becb683f --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/export/Export.java @@ -0,0 +1,216 @@ +package org.jeecg.modules.kc.config.export; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.system.query.QueryGenerator; +import org.jeecg.common.system.vo.DictModel; +import org.jeecg.common.util.SpringContextHolder; +import org.jeecg.modules.kc.config.entity.KcExportConfigTpkwcqkjzglx; +import org.jeecg.modules.kc.config.service.IKcExportConfigTpkwcqkjzglxService; +import org.jeecg.modules.kc.grab.SynchronizationService.tools.ChangeTingKeTongJi; +import org.jeecg.modules.kc.grab.imports.service.IXxhbuserService; +import org.jeecg.modules.kc.kcXqxnHistory.entity.KcXqxnHistory; +import org.jeecg.modules.kc.tksf.kctkcstj.entity.KcTkcstj; +import org.jeecg.modules.kc.tksf.kctkcstj.service.IKcTkcstjService; +import org.jeecg.modules.system.service.ISysDictService; +import org.jeecg.modules.tools.word.BaseExport; +import org.jeecg.modules.tools.word.ExportWord; +import org.jeecg.modules.tools.word.WordOperator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +/** +* 功能说明:导出封装 +* 创建者:byx +* 创建时间:2021-6-30 +*
+* 修改时间:       修改者:            
+* 修改内容:
+* 
+*/ +public class Export extends BaseExport { + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + //由于前面调用使用的是new,所以只能手动注入bean + //字典 + private ISysDictService sysDictService = SpringContextHolder.getBean(ISysDictService.class); + + private IKcExportConfigTpkwcqkjzglxService kcExportConfigTpkwcqkjzglxService = SpringContextHolder.getBean(IKcExportConfigTpkwcqkjzglxService.class); + + private IXxhbuserService xxhbuserService = SpringContextHolder.getBean(IXxhbuserService.class); + + private IKcTkcstjService kcTkcstjService = SpringContextHolder.getBean(IKcTkcstjService.class); + + private ChangeTingKeTongJi changeTingKeTongJi = SpringContextHolder.getBean(ChangeTingKeTongJi.class); + +// /** +// * +// * 功能说明 : 将导出的文档片段按照list返回 +// * 创建者 : byx +// * 修改日期 : 2018年9月26日 +// * @param +// * @return 文档片段的list对象 +// * @throws Exception +// */ +// public List exportAll(QnCustomerHetong customerHetong, SysUser user, Date now) throws Exception{ +// List list = Lists.newArrayList(); +// list.add(export1(customerHetong,user,now)); +// list.add(export2(customerHetong)); +// list.add(export3(customerHetong)); +// return list; +// } + + @Override + public List getDictList(String dictCode){ + return sysDictService.queryDictItemsByCode(dictCode); + } + + /** + * + * 功能说明 : 拼接word导出 + * 创建者 : byx + * 修改日期 : 2021-6-30 + * @param xqxnParam + * @return + * @throws Exception + */ + public WordOperator export(KcExportConfigTpkwcqkjzglx kcExportConfigTpkwcqkjzglx, String xqxnParam) throws Exception{ + String templateName = "exp1\\tpkqk.docx"; + + + Map>> mainMapList = Maps.newHashMap(); + + WordOperator wo = ExportWord.getWordOperator(templateName); + //********************************@A.单个内容替换***************************************************/ + //********************************取数据***********************************************************/ + Map result = Maps.newHashMap(); + //取配置表中的用户 + QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper("a", kcExportConfigTpkwcqkjzglx,null); + queryWrapper.eq(StringUtils.isNotBlank(kcExportConfigTpkwcqkjzglx.getDwmc()),"b.dwmc",kcExportConfigTpkwcqkjzglx.getDwmc()); + List KcExportConfigTpkwcqkjzglxList = kcExportConfigTpkwcqkjzglxService.list(queryWrapper); + Map> ghMap = KcExportConfigTpkwcqkjzglxList.stream().collect(Collectors.groupingBy(KcExportConfigTpkwcqkjzglx::getGh)); + Map> typeMap = KcExportConfigTpkwcqkjzglxList.stream().collect(Collectors.groupingBy(KcExportConfigTpkwcqkjzglx::getTklx)); + + + KcXqxnHistory common = changeTingKeTongJi.getXqConfig(xqxnParam); + if(StringUtils.isBlank(xqxnParam)){ + xqxnParam = common.getTitle(); + } +// Date startSjDate = common.getStartTime(); +// Date endSjDate = common.getEndTime(); +// String startSj = DateUtil.format(startSjDate, DatePattern.NORM_DATETIME_FORMAT); +// String endSj = DateUtil.format(endSjDate,DatePattern.NORM_DATETIME_FORMAT); +// String xqxn = common.getTitle(); +// if(StringUtils.equals(xqxn,kcEvaluation.getXnxq())){ + + + QueryWrapper tjQueryWrapper = new QueryWrapper<>(); + tjQueryWrapper.in("jgh",ghMap.keySet()); +// queryWrapper.apply("up_date >= STR_TO_DATE('"+ startSj +"', '%Y-%m-%d') "); +// queryWrapper.apply("up_date <= STR_TO_DATE('"+ endSj +"', '%Y-%m-%d') "); + tjQueryWrapper.eq("xnxq",xqxnParam); + List tktjList = kcTkcstjService.list(tjQueryWrapper); + + Map tktjMap = tktjList.stream().collect(Collectors.toMap(KcTkcstj::getJgh,a -> a, (a,b) -> a)); + +// QueryWrapper uqw = new QueryWrapper(); +// uqw.in("gh",typeMap.keySet()); +// List xxhbuserList = xxhbuserService.list(uqw); + + List oneList = typeMap.get("1"); + if(oneList == null || oneList.isEmpty()) oneList = Lists.newArrayList(); + + List twoList = Lists.newArrayList(); + List twoList1 = typeMap.get("2"); + List twoList2 = typeMap.get("3"); + List twoList3 = typeMap.get("4"); + + if(twoList1 != null && !twoList1.isEmpty()) { + twoList.addAll(twoList1); + } + if(twoList2 != null && !twoList2.isEmpty()) { + twoList.addAll(twoList2); + } + if(twoList3 != null && !twoList3.isEmpty()) { + twoList.addAll(twoList3); + } + + List threeList = Lists.newArrayList(); + List threeList1 = typeMap.get("5"); + List threeList2 = typeMap.get("6"); + + if(threeList1 != null && !threeList1.isEmpty()) { + threeList.addAll(threeList1); + } + if(threeList2 != null && !threeList2.isEmpty()) { + threeList.addAll(threeList2); + } + //********************************取数据END*********************************************************/ + //********************************通用*************************************************************/ + result.put("xqxn",xqxnParam); + //填进去数量 + result.put("oneListSize", String.valueOf(oneList.size())); + result.put("twoListSize", String.valueOf(twoList.size())); + result.put("threeListSize", String.valueOf(threeList.size())); + + //********************************表头END**********************************************************/ + wo.replaceTextPlus(result); + //********************************@A.单个内容替换END**************************************************/ + int index = 0; + List> mainList = Lists.newArrayList(); + + oneList.forEach(x -> { + if(tktjMap.containsKey(x.getGh())) { + List tmpList = new ArrayList<>(); + mainList.add(tmpList); + KcTkcstj tkcstj = tktjMap.get(x.getGh()); + tmpList.add(null); + tmpList.add(StringUtils.defaultString(tkcstj.getJsxm(),x.getXm())); + tmpList.add(tkcstj.getTkxttj()); + } + }); + index = 4; + //从固定位置开始添加(1为第二个表格,第二个1为固定位置) + wo.insert2Table(0, index, true, mainList); + + index += mainList.size() + 2; + mainList.clear(); + twoList.forEach(x -> { + if(tktjMap.containsKey(x.getGh())) { + List tmpList = new ArrayList<>(); + mainList.add(tmpList); + KcTkcstj tkcstj = tktjMap.get(x.getGh()); + tmpList.add(null); + tmpList.add(StringUtils.defaultString(tkcstj.getJsxm(),x.getXm())); + tmpList.add(tkcstj.getTkxttj()); + } + }); + //从固定位置开始添加(1为第二个表格,第二个1为固定位置) + wo.insert2Table(0, index, true, mainList); + + index += mainList.size() + 2; + mainList.clear(); + threeList.forEach(x -> { + if(tktjMap.containsKey(x.getGh())) { + List tmpList = new ArrayList<>(); + mainList.add(tmpList); + KcTkcstj tkcstj = tktjMap.get(x.getGh()); + tmpList.add(null); + tmpList.add(StringUtils.defaultString(tkcstj.getJsxm(),x.getXm())); + tmpList.add(tkcstj.getTkxttj()); + } + }); + //从固定位置开始添加(1为第二个表格,第二个1为固定位置) + wo.insert2Table(0, index, true, mainList); + return wo; + } +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/mapper/KcExportConfigTpkwcqkjzglxMapper.java b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/mapper/KcExportConfigTpkwcqkjzglxMapper.java new file mode 100644 index 00000000..515f3651 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/mapper/KcExportConfigTpkwcqkjzglxMapper.java @@ -0,0 +1,14 @@ +package org.jeecg.modules.kc.config.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.jeecg.modules.kc.config.entity.KcExportConfigTpkwcqkjzglx; + +/** + * @Description: kc_export_config_tpkwcqkjzglx + * @Author: jeecg-boot + * @Date: 2023-07-21 + * @Version: V1.0 + */ +public interface KcExportConfigTpkwcqkjzglxMapper extends BaseMapper { + +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/mapper/xml/KcExportConfigTpkwcqkjzglxMapper.xml b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/mapper/xml/KcExportConfigTpkwcqkjzglxMapper.xml new file mode 100644 index 00000000..18d7d41a --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/mapper/xml/KcExportConfigTpkwcqkjzglxMapper.xml @@ -0,0 +1,27 @@ + + + + + + + + + \ No newline at end of file diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/service/IKcExportConfigTpkwcqkjzglxService.java b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/service/IKcExportConfigTpkwcqkjzglxService.java new file mode 100644 index 00000000..696b741b --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/service/IKcExportConfigTpkwcqkjzglxService.java @@ -0,0 +1,14 @@ +package org.jeecg.modules.kc.config.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.jeecg.modules.kc.config.entity.KcExportConfigTpkwcqkjzglx; + +/** + * @Description: kc_export_config_tpkwcqkjzglx + * @Author: jeecg-boot + * @Date: 2023-07-21 + * @Version: V1.0 + */ +public interface IKcExportConfigTpkwcqkjzglxService extends IService { + +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/service/impl/KcExportConfigTpkwcqkjzglxServiceImpl.java b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/service/impl/KcExportConfigTpkwcqkjzglxServiceImpl.java new file mode 100644 index 00000000..bd4d8912 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/service/impl/KcExportConfigTpkwcqkjzglxServiceImpl.java @@ -0,0 +1,18 @@ +package org.jeecg.modules.kc.config.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.jeecg.modules.kc.config.entity.KcExportConfigTpkwcqkjzglx; +import org.jeecg.modules.kc.config.mapper.KcExportConfigTpkwcqkjzglxMapper; +import org.jeecg.modules.kc.config.service.IKcExportConfigTpkwcqkjzglxService; +import org.springframework.stereotype.Service; + +/** + * @Description: kc_export_config_tpkwcqkjzglx + * @Author: jeecg-boot + * @Date: 2023-07-21 + * @Version: V1.0 + */ +@Service +public class KcExportConfigTpkwcqkjzglxServiceImpl extends ServiceImpl implements IKcExportConfigTpkwcqkjzglxService { + +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/vue3Native/KcExportConfigTpkwcqkjzglx.api.ts b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/vue3Native/KcExportConfigTpkwcqkjzglx.api.ts new file mode 100644 index 00000000..c979745c --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/vue3Native/KcExportConfigTpkwcqkjzglx.api.ts @@ -0,0 +1,72 @@ +import { defHttp } from '/@/utils/http/axios'; +import { useMessage } from "/@/hooks/web/useMessage"; + +const { createConfirm } = useMessage(); + +enum Api { + list = '/config/kcExportConfigTpkwcqkjzglx/list', + save='/config/kcExportConfigTpkwcqkjzglx/add', + edit='/config/kcExportConfigTpkwcqkjzglx/edit', + deleteOne = '/config/kcExportConfigTpkwcqkjzglx/delete', + deleteBatch = '/config/kcExportConfigTpkwcqkjzglx/deleteBatch', + importExcel = '/config/kcExportConfigTpkwcqkjzglx/importExcel', + exportXls = '/config/kcExportConfigTpkwcqkjzglx/exportXls', +} + +/** + * 导出api + * @param params + */ +export const getExportUrl = Api.exportXls; + +/** + * 导入api + */ +export const getImportUrl = Api.importExcel; + +/** + * 列表接口 + * @param params + */ +export const list = (params) => defHttp.get({ url: Api.list, params }); + +/** + * 删除单个 + * @param params + * @param handleSuccess + */ +export const deleteOne = (params,handleSuccess) => { + return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => { + handleSuccess(); + }); +} + +/** + * 批量删除 + * @param params + * @param handleSuccess + */ +export const batchDelete = (params, handleSuccess) => { + createConfirm({ + iconType: 'warning', + title: '确认删除', + content: '是否删除选中数据', + okText: '确认', + cancelText: '取消', + onOk: () => { + return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => { + handleSuccess(); + }); + } + }); +} + +/** + * 保存或者更新 + * @param params + * @param isUpdate + */ +export const saveOrUpdate = (params, isUpdate) => { + let url = isUpdate ? Api.edit : Api.save; + return defHttp.post({ url: url, params }, { isTransformResponse: false }); +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/vue3Native/KcExportConfigTpkwcqkjzglx.data.ts b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/vue3Native/KcExportConfigTpkwcqkjzglx.data.ts new file mode 100644 index 00000000..96e6772f --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/vue3Native/KcExportConfigTpkwcqkjzglx.data.ts @@ -0,0 +1,66 @@ +import {BasicColumn} from '/@/components/Table'; +import {FormSchema} from '/@/components/Table'; +import { rules} from '/@/utils/helper/validator'; +import { render } from '/@/utils/common/renderUtils'; +//列表数据 +export const columns: BasicColumn[] = [ + { + title: '工号', + align: "center", + dataIndex: 'gh_dictText' + }, + { + title: '听课类型(字典:tpkwcqkjzglx )', + align: "center", + dataIndex: 'tklx_dictText' + }, +]; + +//查询数据 +export const searchFormSchema: FormSchema[] = [ + { + label: "工号", + field: 'gh', + component: 'JDictSelectTag', + componentProps:{ + dictCode: "xxhbuser,xm,gh" + }, + colProps: {span: 6}, + }, + { + label: "听课类型(字典:tpkwcqkjzglx )", + field: 'tklx', + component: 'JDictSelectTag', + componentProps:{ + dictCode: "tpkwcqkjzglx" + }, + colProps: {span: 6}, + }, +]; + +//表单数据 +export const formSchema: FormSchema[] = [ + { + label: '工号', + field: 'gh', + component: 'JDictSelectTag', + componentProps:{ + dictCode: "xxhbuser,xm,gh" + }, + }, + { + label: '听课类型(字典:tpkwcqkjzglx )', + field: 'tklx', + component: 'JDictSelectTag', + componentProps:{ + dictCode: "tpkwcqkjzglx" + }, + }, + // TODO 主键隐藏字段,目前写死为ID + { + label: '', + field: 'id', + component: 'Input', + show: false, + }, +]; diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/vue3Native/KcExportConfigTpkwcqkjzglxList.vue b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/vue3Native/KcExportConfigTpkwcqkjzglxList.vue new file mode 100644 index 00000000..dc2fccbb --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/vue3Native/KcExportConfigTpkwcqkjzglxList.vue @@ -0,0 +1,238 @@ + + + + + diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/vue3Native/components/KcExportConfigTpkwcqkjzglxForm.vue b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/vue3Native/components/KcExportConfigTpkwcqkjzglxForm.vue new file mode 100644 index 00000000..69139bb7 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/vue3Native/components/KcExportConfigTpkwcqkjzglxForm.vue @@ -0,0 +1,134 @@ + + + + + diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/vue3Native/components/KcExportConfigTpkwcqkjzglxModal.vue b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/vue3Native/components/KcExportConfigTpkwcqkjzglxModal.vue new file mode 100644 index 00000000..e85b7ac1 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/kc/config/vue3Native/components/KcExportConfigTpkwcqkjzglxModal.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/CommonUtil.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/CommonUtil.java new file mode 100644 index 00000000..7dc18886 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/CommonUtil.java @@ -0,0 +1,79 @@ +package org.jeecg.modules.tools; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.StringEscapeUtils; +import org.jeecg.common.util.CommonUtils; + +import java.util.Map; + +public class CommonUtil extends CommonUtils { + + public static String getFileName(String fileName) { + int unixSep = fileName.lastIndexOf(47); + int winSep = fileName.lastIndexOf(92); + int pos = winSep > unixSep ? winSep : unixSep; + if (pos != -1) { + fileName = fileName.substring(pos + 1); + } + + fileName = fileName.replace("=", "").replace(",", "").replace("&", "").replace("#", ""); + //if (ifContainChinese(fileName)) { + //fileName = PinyinUtil.getPinyin(fileName, ""); + //} + + fileName = fileName.replaceAll("\\s", ""); + return fileName; + } + + + + /** + * 替换一段文字里有多个{key1},{key2} + * @param str 待替换的字符 + * @param replaceMap 替换的map + * @param startKey 开始的键值 + * @param endKey 结束的键值 + * @return + */ + public static String getReplaceMapValue(String str, Map replaceMap, String startKey, String endKey){ + if(StringUtils.isNotBlank(str)){ + //之前判断过,现在不再判断是否存在两个{ + //判断是否存在第二个{,如“姓名:{name},{miniName}”,存在则替换其字符 + StringBuilder sb = new StringBuilder(); + //“姓名:{name},{miniName}xxxxx”拆分成“['姓名:','name},','miniName}','xxxxx']” + String[] arr = StringUtils.split(str,startKey); + for (String s:arr){ + int endIndex = StringUtils.indexOf(s,endKey); + if(endIndex != -1){ + //包含key和额外的, + String[] keyList = StringUtils.split(s,endKey); + for (int i=0;i + * 在clazz与o不是一种对象时使用
+ * 如果需要保持一致请使用{@link org.jeecg.modules.tools.DictUtils#translateDictSelf} + * @param o 传入的是单个entity对象 + * @param clazz 返回对象的类型 + * @return 传入的clazz类型的新对象 + * @throws IllegalAccessException + */ + public T translateDict(Object o,Class clazz) throws IllegalAccessException { + Field[] fields = ReflectUtil.getFields(clazz); + T rObject = ReflectUtil.newInstance(clazz); + //将所有内容转入新对象里 + BeanUtil.copyProperties(o,rObject); + return translateDictSelf(rObject,clazz); + } + + /** + * 翻译字典,支持@Dict的翻译,翻译出的字段放在有@Dict字段的名字 + "_dictText"
+ * 此方法返回自己,保证是同一个对象,以方便list使用
+ * 如果需要保持一致请使用{@link org.jeecg.modules.tools.DictUtils#translateDict} + * @param o 传入的是单个entity对象,也是返回的对象 + * @param clazz 返回对象的类型 + * @return 返回的是变量o这个对象 + * @throws IllegalAccessException + */ + public T translateDictSelf(T o,Class clazz) throws IllegalAccessException { + Field[] fields = ReflectUtil.getFields(clazz); + for (Field field:fields){ + if (field.getAnnotation(Dict.class) != null) { + boolean accessible = field.isAccessible(); + field.setAccessible(true); + String key = String.valueOf(field.get(o)); + field.setAccessible(accessible); + String textValue = getDictTxt(field,key); + Field newField = ReflectUtil.getField(clazz,field.getName() + "_dictText"); + //为空则赋值失败 + if(newField != null){ + boolean accessibleNew = newField.isAccessible(); + newField.setAccessible(true); + newField.set(o,textValue); + newField.setAccessible(accessibleNew); + }else{ + log.error("字段【{}{}】不存在!值为:{} ",field.getName(),"_dictText",textValue); + } + } + } + return o; + } + + /** + * 获取根据属性和真实的值获取字典值 + * @param field 反射出来的类属性 + * @param key 查询的值 + * @return 字典文本 + */ + private String getDictTxt(Field field,String key){ + boolean accessible = field.isAccessible(); + field.setAccessible(true); + String code = field.getAnnotation(Dict.class).dicCode(); + String text = field.getAnnotation(Dict.class).dicText(); + String table = field.getAnnotation(Dict.class).dictTable(); + String textValue = translateDictValue(code, text, table, key); + log.debug(" 字典Val : " + textValue); + log.debug(" __翻译字典字段__ " + field.getName() + "_dictText" + ": " + textValue); + field.setAccessible(accessible); + return textValue; + } + + /** + * 翻译字典 + * @param code 字典code字段 + * @param key 字典查询的值 + * @return 字典文本 + */ + public String translateDictValue(String code, String key) { + return translateDictValue(code,"","",key); + } + + /** + * 翻译字典 + * @param code 字典code字段 + * @param text 字典文字字段 + * @param table 字典表名 + * @param key 字典查询的值 + * @return 字典文本 + */ + public String translateDictValue(String code, String text, String table, String key) { + if (oConvertUtils.isEmpty(key)) { + return null; + } else { + StringBuilder textValue = new StringBuilder(); + String[] keys = key.split(","); + for(int i = 0; i < keys.length; ++i) { + String k = keys[i]; + String tmpValue; + log.debug(" 字典 key : " + k); + if (k.trim().length() != 0) { + if (!StringUtils.isEmpty(table)) { + log.debug("--DictUtils------dicTable=" + table + " ,dicText= " + text + " ,dicCode=" + code); + tmpValue = this.commonAPI.translateDictFromTable(table, text, code, k.trim()); + } else { + tmpValue = this.commonAPI.translateDict(code, k.trim()); + } + if (tmpValue != null) { + if (!"".equals(textValue.toString())) { + textValue.append(","); + } + textValue.append(tmpValue); + } + } + } + return textValue.toString(); + } + } +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/FilePreviewUtils.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/FilePreviewUtils.java new file mode 100644 index 00000000..379f0dae --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/FilePreviewUtils.java @@ -0,0 +1,397 @@ +package org.jeecg.modules.tools; + +import cn.hutool.core.lang.UUID; +import org.springframework.core.SpringProperties; + +import java.io.File; +import java.util.Date; + +/** + * 文件预览(文档转换) + */ +public class FilePreviewUtils { + + private static final int wdFormatPDF = 17; + private static final int xlTypePDF = 0; + private static final int ppSaveAsPDF = 32; + + public static void mains(String[] args) { + + /* + * 使用方式在\static\filetransform中 请自行参考 + * + * + * 运行完毕后台会对应打出字幕(运行完成) 完成后 在\static\filetransform中找到静态页面 + * example.html(可粘贴复制到桌面 里面对应链接自行更改(更改之后 运行显示内容) ) + * + * + * 项目本地实例文件名字 example111word.doc example222word.docx example111ppt.ppt + * example222ppt.pptx example111exsl.xls example222exsl.xlsx + */ + String fileName = "example111word.doc";// 文件名字随影更换 + // String newFileName=fileName.substring(0, + // fileName.lastIndexOf("."));//生成新文件名字 + // 获取个人生成代码的项目位置(注意生成代码位置一定不要填错) + String a = SpringProperties.getProperty("projectPath") + "\\WebContent\\static\\filetransform\\examplefile\\"; + String newUrl = a.replace("\\", "/"); + // 文件全路径 和生成的文件名字 + int time = convert2PDF(newUrl + fileName, + "生成pdf文件地址 (生成的地址是 tomcat wepapps下 自己创建的pdf.js文件夹下 详细参考\\static\\filetransform\\usagemethod\\必读.txt)"); + if (time == -4) { + System.out.println("转化失败,未知错误..."); + } else if (time == -3) { + System.out.println("原文件就是PDF文件,无需转化..."); + } else if (time == -2) { + System.out.println("转化失败,文件不存在..."); + } else if (time == -1) { + System.out.println("转化失败,请重新尝试..."); + } else if (time < -4) { + System.out.println("转化失败,请重新尝试..."); + } else { + System.out.println("转化成功,用时: " + time + "s..."); + } + + } + + public static void main(String[] args) { + String fileUrl = "E:/bak/fileresource/userresource/1/111 - “”副——本.doc"; + fileUrl = "/userresource/1/111 - “+”副——本.doc"; +// MAP M = SYSTEM.GETENV(); +// +// SYSTEM.OUT.PRINTLN( M.GET("CATALINA_HOME")); +// File file = new File("."); +// String path = file.getParent().getAbsolutePath(); +// System.out.println(System.getProperty("catalina.home")); +// FilePreviewUtils a = new FilePreviewUtils(); +// System.out.println(a.getTomcatPath()); + + // ="D:/fileresource/userSwapper/4716fb99f8794a8e8c5d31e428a9f851/门户修改意见.pdf"; + System.out.println(FilePreviewUtils.init(fileUrl, "education")); + } + + public String getTomcatPath(){ + String nodepath = this.getClass().getClassLoader().getResource("/").getPath(); + // 项目的根目录路径 + String filePath = nodepath.substring(1, nodepath.length() - 16); + return filePath; + } + + /** + * + * + * @param fileUrl:文件访问URL路径 + * @param projectName:项目名称 + * @return 转换状态str,-100,参数为空 + */ + public static String init(String fileUrl, String projectName) { + int msg = -100; + if ((fileUrl == null && "".equals(fileUrl))) { + return msg + "|-1"; + } + FilePreviewUtils thisx = new FilePreviewUtils(); + System.out.println("Tomcat路径--" + thisx.getTomcatPath()); + // String path = fileUrl; + String path = SpringProperties.getProperty("fileResource.basedir") + fileUrl; + + String filePath = thisx.getTomcatPath() + "" + SpringProperties.getProperty("pdfProject.name");// + String[] _fileName = fileUrl.split("/"); + // String[] _fileType ={}; + String fileName = ""; + String fileDir = ""; + if (_fileName.length > 0) { + // _fileType = _fileName[_fileName.length-1].split("\\."); + // fileName = _fileType[0]+".pdf"; + fileName = UUID.randomUUID() + ".pdf"; + fileDir = filePath; + filePath += fileName; + } + // 判断有没有文件夹 + File dir = new File(fileDir); + judeDirExists(dir); + System.out.println("资源路径--" + path); + System.out.println("生成路径--" + filePath); + System.out.println("文件夹路径--" + fileDir); + // String fileName="example111word.doc";//文件名字随影更换 + // String newFileName=fileName.substring(0, + // fileName.lastIndexOf("."));//生成新文件名字 + // 获取个人生成代码的项目位置(注意生成代码位置一定不要填错) + // String a = + // SpringProperties.getProperty("projectPath")+"\\WebContent\\static\\filetransform\\examplefile\\"; + String newFileUrl = path.replace("\\", "/"); + String newFilePath = filePath.replace("\\", "/"); + + // 文件全路径 和生成的文件名字 + msg = convert2PDF(newFileUrl, newFilePath); + + if (msg == -4) { + System.out.println("转化失败,未知错误..."); + } else if (msg == -3) { + System.out.println("原文件就是PDF文件,无需转化..."); + } else if (msg == -2) { + System.out.println("转化失败,文件不存在..."); + } else if (msg == -1) { + System.out.println("转化失败,请重新尝试..."); + } else if (msg < -4) { + System.out.println("转化失败,请重新尝试..."); + } else { + System.out.println("转化成功,用时: " + msg + "s..."); + } + + return msg + "|" + fileName; + } + + //安装完整路径生成文件 + public static void word2ToPdf(String fileUrl, String newFileUrl) { + int msg = -100; + // 文件全路径 和生成的文件名字 + msg = convert2PDF(fileUrl, newFileUrl); + + if (msg == -4) { + System.out.println("转化失败,未知错误..."); + } else if (msg == -3) { + System.out.println("原文件就是PDF文件,无需转化..."); + } else if (msg == -2) { + System.out.println("转化失败,文件不存在..."); + } else if (msg == -1) { + System.out.println("转化失败,请重新尝试..."); + } else if (msg < -4) { + System.out.println("转化失败,请重新尝试..."); + } else { + System.out.println("转化成功,用时: " + msg + "s..."); + } + } + + /** + * + * + * @param fileUrl:文件访问URL路径 + * @param projectName:项目名称 + * @return 转换状态str,-100,参数为空 + */ + public static String initHome(String fileUrl, String projectName) { + int msg = -100; + if ((fileUrl == null && "".equals(fileUrl))) { + return msg + "|-1"; + } + FilePreviewUtils thisx = new FilePreviewUtils(); + System.out.println("Tomcat路径--" + thisx.getTomcatPath()); + // String path = fileUrl; + String path = SpringProperties.getProperty("userfiles.basedir") + fileUrl; + + String filePath = thisx.getTomcatPath() + "" + SpringProperties.getProperty("pdfProject.name");// + String[] _fileName = fileUrl.split("/"); + // String[] _fileType ={}; + String fileName = ""; + String fileDir = ""; + if (_fileName.length > 0) { + +// String[] _fileType = _fileName[_fileName.length-1].split("\\."); +// _fileTypefileUrl.split("."); +// fileName = _fileType[0]+".pdf"; + fileName = UUID.randomUUID() + ".pdf"; + fileDir = filePath; + filePath += fileName; + } + // 判断有没有文件夹 + File dir = new File(fileDir); + judeDirExists(dir); + System.out.println("资源路径--" + path); + System.out.println("生成路径--" + filePath); + System.out.println("文件夹路径--" + fileDir); + // String fileName="example111word.doc";//文件名字随影更换 + // String newFileName=fileName.substring(0, + // fileName.lastIndexOf("."));//生成新文件名字 + // 获取个人生成代码的项目位置(注意生成代码位置一定不要填错) + // String a = + // SpringProperties.getProperty("projectPath")+"\\WebContent\\static\\filetransform\\examplefile\\"; + String newFileUrl = path.replace("\\", "/"); + String newFilePath = filePath.replace("\\", "/"); + + // 文件全路径 和生成的文件名字 + msg = convert2PDF(newFileUrl, newFilePath); + + if (msg == -4) { + System.out.println("转化失败,未知错误..."); + } else if (msg == -3) { + System.out.println("原文件就是PDF文件,无需转化..."); + } else if (msg == -2) { + System.out.println("转化失败,文件不存在..."); + } else if (msg == -1) { + System.out.println("转化失败,请重新尝试..."); + } else if (msg < -4) { + System.out.println("转化失败,请重新尝试..."); + } else { + System.out.println("转化成功,用时: " + msg + "s..."); + } + + return msg + "|" + fileName; + } + + + + // 获取尾椎名字(判断文件类型) + public static String getFileSufix(String fileName) { + fileName = fileName.toLowerCase();//转化成小写字母 + int splitIndex = fileName.lastIndexOf("."); + + return fileName.substring(splitIndex + 1); + } + + // 主方法调用 + private static int convert2PDF(String inputFile, String pdfFile) { + // 获取后缀名(判断文件类型) + String kind = getFileSufix(inputFile); + File file = new File(inputFile);// 获取文件 + if (!file.exists()) { + return -2;// 文件不存在 + } + // if (kind.equals("pdf")) { + // return -3;//原文件就是PDF文件 + // } + // 验证属于什么类型文件(根据不同文件走不同方法) + if (kind.equals("doc") || kind.equals("docx") || kind.equals("txt")) { + return FilePreviewUtils.word2PDF(inputFile, pdfFile); + } else if (kind.equals("ppt") || kind.equals("pptx")) { + return FilePreviewUtils.ppt2PDF(inputFile, pdfFile); + } else if (kind.equals("xls") || kind.equals("xlsx")) { + return FilePreviewUtils.Ex2PDF(inputFile, pdfFile); + } + if (kind.equals("pdf")) { + return FilePreviewUtils.addPDF(inputFile, pdfFile); + } else { + return -4; + } + } + + private static int word2PDF(String inputFile, String pdfFile) { + try { +// // 打开Word应用程序 +// ActiveXComponent app = new ActiveXComponent("KWPS.Application"); +// System.out.println("开始转化Word为PDF..."); +// long date = new Date().getTime(); +// // 设置Word不可见 +// app.setProperty("Visible", new Variant(false)); +// // 禁用宏 +// app.setProperty("AutomationSecurity", new Variant(3)); +// // 获得Word中所有打开的文档,返回documents对象 +// Dispatch docs = app.getProperty("Documents").toDispatch(); +// // 调用Documents对象中Open方法打开文档,并返回打开的文档对象Document +// Dispatch doc = Dispatch.call(docs, "Open", inputFile, false, true).toDispatch(); +// +// Dispatch.call(doc, "ExportAsFixedFormat", pdfFile, wdFormatPDF);// word保存为pdf格式宏,值为17 +// System.out.println(doc); +// // 关闭文档 +// long date2 = new Date().getTime(); +// int time = (int) ((date2 - date) / 1000); +// +// Dispatch.call(doc, "Close", false); +// // 关闭Word应用程序 +// app.invoke("Quit", 0); +// return time; + return -2; + } catch (Exception e) { + System.out.println(e); + return -1; + } + + } + + private static int Ex2PDF(String inputFile, String pdfFile) { + try { + +// ComThread.InitSTA(true); +// ActiveXComponent ax = new ActiveXComponent("KET.Application"); +// System.out.println("开始转化Excel为PDF..."); +// long date = new Date().getTime(); +// ax.setProperty("Visible", false); +// ax.setProperty("AutomationSecurity", new Variant(3)); // 禁用宏 +// Dispatch excels = ax.getProperty("Workbooks").toDispatch(); +// +// Dispatch excel = Dispatch +// .invoke(excels, "Open", Dispatch.Method, +// new Object[] { inputFile, new Variant(false), new Variant(false) }, new int[9]) +// .toDispatch(); +// // 转换格式 +// Dispatch.invoke(excel, "ExportAsFixedFormat", Dispatch.Method, new Object[] { new Variant(0), // PDF格式=0 +// pdfFile, new Variant(xlTypePDF) // 0=标准 (生成的PDF图片不会变模糊) +// // 1=最小文件 +// }, new int[1]); +// +// long date2 = new Date().getTime(); +// int time = (int) ((date2 - date) / 1000); +// Dispatch.call(excel, "Close", new Variant(false)); +// +// if (ax != null) { +// ax.invoke("Quit", new Variant[] {}); +// ax = null; +// } +// ComThread.Release(); +// return time; + return -2; + } catch (Exception e) { + System.out.println(e); + return -1; + } + } + + private static int ppt2PDF(String inputFile, String pdfFile) { + try { +// ComThread.InitSTA(true); +// ActiveXComponent app = new ActiveXComponent("KWPP.Application"); +// System.out.println("开始转化PPT为PDF..."); +// long date = new Date().getTime(); +// Dispatch ppts = app.getProperty("Presentations").toDispatch(); +// // 打开ppt返回相应对象 +// Dispatch ppt = Dispatch.call(ppts, "Open", inputFile, true, false).toDispatch(); +// // 对应生成pdf +// Dispatch.invoke(ppt, "SaveAs", Dispatch.Method, new Object[] { pdfFile, new Variant(ppSaveAsPDF) }, +// new int[1]); +// System.out.println("PPT"); +// Dispatch.call(ppt, "Close"); +// long date2 = new Date().getTime(); +// int time = (int) ((date2 - date) / 1000); +// app.invoke("Quit"); +// return time;// 运行秒数 + return -2; + } catch (Exception e) { + System.out.println(e); + return -1; + } + } + + private static int addPDF(String inputFile, String pdfFile) { + try { + System.out.println("正在复制PDF文档" + inputFile); + long date = new Date().getTime(); + boolean tf = FileUtils.copyFileCover(inputFile, pdfFile, true); + System.out.println("复制成功" + pdfFile); + long date2 = new Date().getTime(); + int time = (int) ((date2 - date) / 1000); + if (tf) { + return time; + } else { + return -1; + } + } catch (Exception e) { + System.out.println(e); + return -1; + } + } + + // 判断文件夹是否存在 + public static void judeDirExists(File file) { + + if (file.exists()) { + if (file.isDirectory()) { + System.out.println("文件夹已存在"); + } else { + System.out.println("同名文件夹已存在,无法创建目录"); + } + } else { + System.out.println("目录不存在,创建中。。。"); + file.mkdir(); + } + + } + +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/FileUtils.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/FileUtils.java new file mode 100644 index 00000000..d911d055 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/FileUtils.java @@ -0,0 +1,761 @@ +/** + * Copyright © 2015-2020 JeePlus All rights reserved. + */ +package org.jeecg.modules.tools; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.util.DateUtils; +import org.jeecg.modules.tools.word.WordOperator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.http.HttpServletRequest; +import java.io.*; +import java.util.Arrays; +import java.util.Date; +import java.util.Enumeration; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +/** + * 文件操作工具类 + * 实现文件的创建、删除、复制、压缩、解压以及目录的创建、删除、复制、压缩解压等功能 + * @author jeeplus + * @version 2013-06-21 + */ +public class FileUtils extends org.apache.commons.io.FileUtils { + + private static Logger log = LoggerFactory.getLogger(FileUtils.class); + + /** + * 复制单个文件,如果目标文件存在,则不覆盖 + * @param srcFileName 待复制的文件名 + * @param descFileName 目标文件名 + * @return 如果复制成功,则返回true,否则返回false + */ + public static boolean copyFile(String srcFileName, String descFileName) { + return FileUtils.copyFileCover(srcFileName, descFileName, false); + } + + /** + * 复制到输出流 + * @param file + * @param os + * @return + */ + public static boolean copyFile(File file, OutputStream os, boolean nulVal) { + boolean isSuccess = false; + InputStream is = null; + try { + is = openInputStream(file); + IoUtil.copy(is,os,IoUtil.DEFAULT_BUFFER_SIZE); +// IOUtils.write(readFileToString(file),os); + isSuccess = true; + } catch (IOException e) { + log.error(e.getMessage(),e); + }finally { + IoUtil.close(is); + } + return isSuccess; + } + + + /** + * 复制单个文件 + * @param srcFileName 待复制的文件名 + * @param descFileName 目标文件名 + * @param coverlay 如果目标文件已存在,是否覆盖 + * @return 如果复制成功,则返回true,否则返回false + */ + public static boolean copyFileCover(String srcFileName, + String descFileName, boolean coverlay) { + File srcFile = new File(srcFileName); + // 判断源文件是否存在 + if (!srcFile.exists()) { + log.debug("复制文件失败,源文件 " + srcFileName + " 不存在!"); + return false; + } + // 判断源文件是否是合法的文件 + else if (!srcFile.isFile()) { + log.debug("复制文件失败," + srcFileName + " 不是一个文件!"); + return false; + } + File descFile = new File(descFileName); + // 判断目标文件是否存在 + if (descFile.exists()) { + // 如果目标文件存在,并且允许覆盖 + if (coverlay) { + log.debug("目标文件已存在,准备删除!"); + if (!FileUtils.delFile(descFileName)) { + log.debug("删除目标文件 " + descFileName + " 失败!"); + return false; + } + } else { + log.debug("复制文件失败,目标文件 " + descFileName + " 已存在!"); + return false; + } + } else { + if (!descFile.getParentFile().exists()) { + // 如果目标文件所在的目录不存在,则创建目录 + log.debug("目标文件所在的目录不存在,创建目录!"); + // 创建目标文件所在的目录 + if (!descFile.getParentFile().mkdirs()) { + log.debug("创建目标文件所在的目录失败!"); + return false; + } + } + } + + // 准备复制文件 + // 读取的位数 + int readByte = 0; + InputStream ins = null; + OutputStream outs = null; + try { + // 打开源文件 + ins = new FileInputStream(srcFile); + // 打开目标文件的输出流 + outs = new FileOutputStream(descFile); + byte[] buf = new byte[1024]; + // 一次读取1024个字节,当readByte为-1时表示文件已经读取完毕 + while ((readByte = ins.read(buf)) != -1) { + // 将读取的字节流写入到输出流 + outs.write(buf, 0, readByte); + } + log.debug("复制单个文件 " + srcFileName + " 到" + descFileName + + "成功!"); + return true; + } catch (Exception e) { + log.debug("复制文件失败:" + e.getMessage()); + return false; + } finally { + // 关闭输入输出流,首先关闭输出流,然后再关闭输入流 + if (outs != null) { + try { + outs.close(); + } catch (IOException oute) { + oute.printStackTrace(); + } + } + if (ins != null) { + try { + ins.close(); + } catch (IOException ine) { + ine.printStackTrace(); + } + } + } + } + + /** + * 复制整个目录的内容,如果目标目录存在,则不覆盖 + * @param srcDirName 源目录名 + * @param descDirName 目标目录名 + * @return 如果复制成功返回true,否则返回false + */ + public static boolean copyDirectory(String srcDirName, String descDirName) { + return FileUtils.copyDirectoryCover(srcDirName, descDirName, + false); + } + + /** + * 复制整个目录的内容 + * @param srcDirName 源目录名 + * @param descDirName 目标目录名 + * @param coverlay 如果目标目录存在,是否覆盖 + * @return 如果复制成功返回true,否则返回false + */ + public static boolean copyDirectoryCover(String srcDirName, + String descDirName, boolean coverlay) { + File srcDir = new File(srcDirName); + // 判断源目录是否存在 + if (!srcDir.exists()) { + log.debug("复制目录失败,源目录 " + srcDirName + " 不存在!"); + return false; + } + // 判断源目录是否是目录 + else if (!srcDir.isDirectory()) { + log.debug("复制目录失败," + srcDirName + " 不是一个目录!"); + return false; + } + // 如果目标文件夹名不以文件分隔符结尾,自动添加文件分隔符 + String descDirNames = descDirName; + if (!descDirNames.endsWith(File.separator)) { + descDirNames = descDirNames + File.separator; + } + File descDir = new File(descDirNames); + // 如果目标文件夹存在 + if (descDir.exists()) { + if (coverlay) { + // 允许覆盖目标目录 + log.debug("目标目录已存在,准备删除!"); + if (!FileUtils.delFile(descDirNames)) { + log.debug("删除目录 " + descDirNames + " 失败!"); + return false; + } + } else { + log.debug("目标目录复制失败,目标目录 " + descDirNames + " 已存在!"); + return false; + } + } else { + // 创建目标目录 + log.debug("目标目录不存在,准备创建!"); + if (!descDir.mkdirs()) { + log.debug("创建目标目录失败!"); + return false; + } + + } + + boolean flag = true; + // 列出源目录下的所有文件名和子目录名 + File[] files = srcDir.listFiles(); + for (int i = 0; i < files.length; i++) { + // 如果是一个单个文件,则直接复制 + if (files[i].isFile()) { + flag = FileUtils.copyFile(files[i].getAbsolutePath(), + descDirName + files[i].getName()); + // 如果拷贝文件失败,则退出循环 + if (!flag) { + break; + } + } + // 如果是子目录,则继续复制目录 + if (files[i].isDirectory()) { + flag = FileUtils.copyDirectory(files[i] + .getAbsolutePath(), descDirName + files[i].getName()); + // 如果拷贝目录失败,则退出循环 + if (!flag) { + break; + } + } + } + + if (!flag) { + log.debug("复制目录 " + srcDirName + " 到 " + descDirName + " 失败!"); + return false; + } + log.debug("复制目录 " + srcDirName + " 到 " + descDirName + " 成功!"); + return true; + + } + + /** + * + * 删除文件,可以删除单个文件或文件夹 + * + * @param fileName 被删除的文件名 + * @return 如果删除成功,则返回true,否是返回false + */ + public static boolean delFile(String fileName) { + File file = new File(fileName); + if (!file.exists()) { + log.debug(fileName + " 文件不存在!"); + return true; + } else { + if (file.isFile()) { + return FileUtils.deleteFile(fileName); + } else { + return FileUtils.deleteDirectory(fileName); + } + } + } + + /** + * + * 删除单个文件 + * + * @param fileName 被删除的文件名 + * @return 如果删除成功,则返回true,否则返回false + */ + public static boolean deleteFile(String fileName) { + File file = new File(fileName); + if (file.exists() && file.isFile()) { + if (file.delete()) { + log.debug("删除文件 " + fileName + " 成功!"); + return true; + } else { + log.debug("删除文件 " + fileName + " 失败!"); + return false; + } + } else { + log.debug(fileName + " 文件不存在!"); + return true; + } + } + + /** + * + * 删除目录及目录下的文件 + * + * @param dirName 被删除的目录所在的文件路径 + * @return 如果目录删除成功,则返回true,否则返回false + */ + public static boolean deleteDirectory(String dirName) { + String dirNames = dirName; + if (!dirNames.endsWith(File.separator)) { + dirNames = dirNames + File.separator; + } + File dirFile = new File(dirNames); + if (!dirFile.exists() || !dirFile.isDirectory()) { + log.debug(dirNames + " 目录不存在!"); + return true; + } + boolean flag = true; + // 列出全部文件及子目录 + File[] files = dirFile.listFiles(); + for (int i = 0; i < files.length; i++) { + // 删除子文件 + if (files[i].isFile()) { + flag = FileUtils.deleteFile(files[i].getAbsolutePath()); + // 如果删除文件失败,则退出循环 + if (!flag) { + break; + } + } + // 删除子目录 + else if (files[i].isDirectory()) { + flag = FileUtils.deleteDirectory(files[i] + .getAbsolutePath()); + // 如果删除子目录失败,则退出循环 + if (!flag) { + break; + } + } + } + + if (!flag) { + log.debug("删除目录失败!"); + return false; + } + // 删除当前目录 + if (dirFile.delete()) { + log.debug("删除目录 " + dirName + " 成功!"); + return true; + } else { + log.debug("删除目录 " + dirName + " 失败!"); + return false; + } + + } + + /** + * 创建单个文件 + * @param descFileName 文件名,包含路径 + * @return 如果创建成功,则返回true,否则返回false + */ + public static boolean createFile(String descFileName) { + File file = new File(descFileName); + if (file.exists()) { + log.debug("文件 " + descFileName + " 已存在!"); + return false; + } + if (descFileName.endsWith(File.separator)) { + log.debug(descFileName + " 为目录,不能创建目录!"); + return false; + } + if (!file.getParentFile().exists()) { + // 如果文件所在的目录不存在,则创建目录 + if (!file.getParentFile().mkdirs()) { + log.debug("创建文件所在的目录失败!"); + return false; + } + } + + // 创建文件 + try { + if (file.createNewFile()) { + log.debug(descFileName + " 文件创建成功!"); + return true; + } else { + log.debug(descFileName + " 文件创建失败!"); + return false; + } + } catch (Exception e) { + e.printStackTrace(); + log.debug(descFileName + " 文件创建失败!"); + return false; + } + + } + + /** + * 创建目录 + * @param descDirName 目录名,包含路径 + * @return 如果创建成功,则返回true,否则返回false + */ + public static boolean createDirectory(String descDirName) { + String descDirNames = descDirName; + if (!descDirNames.endsWith(File.separator)) { + descDirNames = descDirNames + File.separator; + } + File descDir = new File(descDirNames); + if (descDir.exists()) { + log.debug("目录 " + descDirNames + " 已存在!"); + return false; + } + // 创建目录 + if (descDir.mkdirs()) { + log.debug("目录 " + descDirNames + " 创建成功!"); + return true; + } else { + log.debug("目录 " + descDirNames + " 创建失败!"); + return false; + } + + } + + /** + * 写入文件 + * @param fileName 要写入的文件 + */ + public static void writeToFile(String fileName, String content, boolean append) { + try { + FileUtils.write(new File(fileName), content, "utf-8", append); + log.debug("文件 " + fileName + " 写入成功!"); + } catch (IOException e) { + log.debug("文件 " + fileName + " 写入失败! " + e.getMessage()); + } + } + + /** + * 写入文件 + * @param fileName 要写入的文件 + */ + public static void writeToFile(String fileName, String content, String encoding, boolean append) { + try { + FileUtils.write(new File(fileName), content, encoding, append); + log.debug("文件 " + fileName + " 写入成功!"); + } catch (IOException e) { + log.debug("文件 " + fileName + " 写入失败! " + e.getMessage()); + } + } + + /** + * 新加的方法,由于版本变更,不得不新增,补充一个在文件尾部新增内容 + * @param file + * @param content + * @param encoding + * @param append + * @throws IOException + */ + public static void write(File file,String content, String encoding, boolean append) throws IOException{ + OutputStream out = null; + try { + out = openOutputStream(file); + //往文件结尾插入 + if(append){ + //文件是否存在,不是文件夹,允许读取 + if (file.exists() && !file.isDirectory() && file.canRead()){ + //读取文件, + String fileContent = readFileToString(file, encoding); + content = fileContent + content; + }else{ + log.warn("文件{}不存在啊喂!",file.getPath()); + } + } + IOUtils.write(content, out, encoding); + } finally { + IOUtils.closeQuietly(out); + } + } + + /** + * 压缩文件或目录 + * @param srcDirName 压缩的根目录 + * @param fileName 根目录下的待压缩的文件名或文件夹名,其中*或""表示跟目录下的全部文件 + * @param descFileName 目标zip文件 + */ + public static void zipFiles(String srcDirName, String fileName, String descFileName) { + // 判断目录是否存在 + if (srcDirName == null) { + log.debug("文件压缩失败,目录 " + srcDirName + " 不存在!"); + return; + } + File fileDir = new File(srcDirName); + if (!fileDir.exists() || !fileDir.isDirectory()) { + log.debug("文件压缩失败,目录 " + srcDirName + " 不存在!"); + return; + } + String dirPath = fileDir.getAbsolutePath(); + File descFile = new File(descFileName); + try { + ZipOutputStream zouts = new ZipOutputStream(new FileOutputStream(descFile)); + if ("*".equals(fileName) || "".equals(fileName)) { + FileUtils.zipDirectoryToZipFile(dirPath, fileDir, zouts); + } else { + File file = new File(fileDir, fileName); + if (file.isFile()) { + FileUtils.zipFilesToZipFile(dirPath, file, zouts); + } else { + FileUtils.zipDirectoryToZipFile(dirPath, file, zouts); + } + } + zouts.close(); + log.debug(descFileName + " 文件压缩成功!"); + } catch (Exception e) { + log.debug("文件压缩失败:" + e.getMessage()); + e.printStackTrace(); + } + + } + + /** + * 解压缩ZIP文件,将ZIP文件里的内容解压到descFileName目录下 + * @param zipFileName 需要解压的ZIP文件 + * @param descFileName 目标文件 + */ + public static boolean unZipFiles(String zipFileName, String descFileName) { + String descFileNames = descFileName; + if (!descFileNames.endsWith(File.separator)) { + descFileNames = descFileNames + File.separator; + } + try { + // 根据ZIP文件创建ZipFile对象 + ZipFile zipFile = new ZipFile(zipFileName); + ZipEntry entry = null; + String entryName = null; + String descFileDir = null; + byte[] buf = new byte[4096]; + int readByte = 0; + // 获取ZIP文件里所有的entry + @SuppressWarnings("rawtypes") +// Enumeration enums = zipFile.getEntries(); + Enumeration enums = zipFile.entries();//TODO 无法确定是否能用 + // 遍历所有entry + while (enums.hasMoreElements()) { + entry = (ZipEntry) enums.nextElement(); + // 获得entry的名字 + entryName = entry.getName(); + descFileDir = descFileNames + entryName; + if (entry.isDirectory()) { + // 如果entry是一个目录,则创建目录 + new File(descFileDir).mkdirs(); + continue; + } else { + // 如果entry是一个文件,则创建父目录 + new File(descFileDir).getParentFile().mkdirs(); + } + File file = new File(descFileDir); + // 打开文件输出流 + OutputStream os = new FileOutputStream(file); + // 从ZipFile对象中打开entry的输入流 + InputStream is = zipFile.getInputStream(entry); + while ((readByte = is.read(buf)) != -1) { + os.write(buf, 0, readByte); + } + os.close(); + is.close(); + } + zipFile.close(); + log.debug("文件解压成功!"); + return true; + } catch (Exception e) { + log.debug("文件解压失败:" + e.getMessage()); + return false; + } + } + + /** + * 将目录压缩到ZIP输出流 + * @param dirPath 目录路径 + * @param fileDir 文件信息 + * @param zouts 输出流 + */ + public static void zipDirectoryToZipFile(String dirPath, File fileDir, + ZipOutputStream zouts) { + if (fileDir.isDirectory()) { + File[] files = fileDir.listFiles(); + // 空的文件夹 + if (files.length == 0) { + // 目录信息 + ZipEntry entry = new ZipEntry(getEntryName(dirPath, fileDir)); + try { + zouts.putNextEntry(entry); + zouts.closeEntry(); + } catch (Exception e) { + e.printStackTrace(); + } + return; + } + + for (int i = 0; i < files.length; i++) { + if (files[i].isFile()) { + // 如果是文件,则调用文件压缩方法 + FileUtils + .zipFilesToZipFile(dirPath, files[i], zouts); + } else { + // 如果是目录,则递归调用 + FileUtils.zipDirectoryToZipFile(dirPath, files[i], + zouts); + } + } + + } + + } + + /** + * 将文件压缩到ZIP输出流 + * @param dirPath 目录路径 + * @param file 文件 + * @param zouts 输出流 + */ + public static void zipFilesToZipFile(String dirPath, File file, + ZipOutputStream zouts) { + FileInputStream fin = null; + ZipEntry entry = null; + // 创建复制缓冲区 + byte[] buf = new byte[4096]; + int readByte = 0; + if (file.isFile()) { + try { + // 创建一个文件输入流 + fin = new FileInputStream(file); + // 创建一个ZipEntry + entry = new ZipEntry(getEntryName(dirPath, file)); + // 存储信息到压缩文件 + zouts.putNextEntry(entry); + // 复制字节到压缩文件 + while ((readByte = fin.read(buf)) != -1) { + zouts.write(buf, 0, readByte); + } + zouts.closeEntry(); + fin.close(); + System.out + .println("添加文件 " + file.getAbsolutePath() + " 到zip文件中!"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + + /** + * 获取待压缩文件在ZIP文件中entry的名字,即相对于跟目录的相对路径名 + * @param dirPath 目录名 + * @param file entry文件名 + * @return + */ + private static String getEntryName(String dirPath, File file) { + String dirPaths = dirPath; + if (!dirPaths.endsWith(File.separator)) { + dirPaths = dirPaths + File.separator; + } + String filePath = file.getAbsolutePath(); + // 对于目录,必须在entry名字后面加上"/",表示它将以目录项存储 + if (file.isDirectory()) { + filePath += "/"; + } + int index = filePath.indexOf(dirPaths); + + return filePath.substring(index + dirPaths.length()); + } + + /** + * 给文件名增加日期,防止重复 + */ + public static String addRandom(String fileName) { + return fileName + "-" + DateUtils.formatDate(new Date(), "yyyyMMddHHmmss"); + } + + /** + * 对fileName进行编码 + */ + public static String encodeFileName(String fileName, HttpServletRequest request) throws Exception { + String agent = request.getHeader("USER-AGENT"); + // IE浏览器 + if ((null != agent && -1 != agent.indexOf("MSIE")) || (null != agent && -1 != agent.indexOf("Trident"))) { + fileName = java.net.URLEncoder.encode(fileName, "UTF-8"); + // 火狐,chrome等 + } else if (null != agent && -1 != agent.indexOf("Mozilla")) { + fileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1"); + } + return fileName; + } + +// /** +// * 修复路径,将 \\ 或 / 等替换为 File.separator +// * @param path +// * @return +// */ +// public static String path(String path){ +// String p = StringUtils.replace(path, "\\", "/"); +// p = StringUtils.join(StringUtils.split(p, "/"), "/"); +// if (!StringUtils.startsWithAny(p, "/") && StringUtils.startsWithAny(path, "\\", "/")){ +// p += "/"; +// } +// if (!StringUtils.endsWithAny(p, "/") && StringUtils.endsWithAny(path, "\\", "/")){ +// p = p + "/"; +// } +// return p; +// } + + /** + * 将对应的文件复制到对应的文件夹里 + * @param files 压缩的文件 + * @param basePath 基础路径 + * @param paths 需要复制的文件 + * @param packageName 文件夹名称 + */ + public static void groupPackage(List files,String uploadPath,String basePath, String paths, String packageName){ + if(StringUtils.isNotBlank(paths)){ + List list = Arrays.asList(paths.split(",")); + String packagePath = basePath + "\\" + packageName; + list.forEach(src -> { + String outPath = src; + int index = StringUtils.indexOfAny(outPath,"\\","/"); + if(index != -1){ + outPath = StringUtils.substring(outPath,index); +// Console.log(StringUtils.substring(outPath,index)); + } + FileUtil.copy(uploadPath + "\\" + src,packagePath + "\\" + outPath,true); + }); + files.add(FileUtil.file(packagePath)); + } + } + + public static void groupPackage(String basePath, WordOperator wo, String packageName,String fileName) throws Exception{ + groupPackage(basePath,wo,packageName,fileName,".docx"); + } + /** + * 将对应的文件复制到对应的文件夹里 + * @param basePath 基础路径 + * @param wo 文档对象 + * @param packageName 文件夹名称 + * @param fileName 文件名 + * @param suffixName 文件名后缀 + */ + public static void groupPackage(String basePath, WordOperator wo, String packageName,String fileName,String suffixName) throws Exception{ + if(wo != null){ + String packagePath = basePath + "\\" + packageName; + String filePath = packagePath + "\\" + fileName + suffixName; + FileUtil.touch(filePath); + OutputStream os = new FileOutputStream(filePath); + wo.write(os); + IoUtil.close(os); + } + } + + /** + * 将对应的文件复制到对应的文件夹里 + * @param basePath 基础路径 + * @param file 压缩的文件 + * @param packageName 文件夹名称 + */ + public static void groupPackage(String basePath, File file, String packageName,String fileName,String suffixName) throws Exception{ + String packagePath = basePath + "\\" + packageName; + String filePath = packagePath + "\\" + fileName + suffixName; + FileUtil.touch(filePath); + OutputStream os = new FileOutputStream(filePath); + InputStream is = new FileInputStream(file); + IoUtil.write(os,true,IoUtil.readBytes(is)); + IoUtil.close(is); + } + +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/Global.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/Global.java new file mode 100644 index 00000000..41118334 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/Global.java @@ -0,0 +1,135 @@ +package org.jeecg.modules.tools; + +import cn.hutool.core.io.resource.ResourceUtil; +import org.apache.shiro.io.ResourceUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.env.Environment; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Component; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +/** + * 配置读取 + */ +@Component +public class Global { + + /** + * 全部参数对象 + */ + private Environment environment; + @Autowired + public void setEnvironment(Environment environment){this.environment = environment;} + + /** + * 上传文件地址 + */ + public String uploadPath; + @Value("${jeecg.path.upload}") + public void setUploadPath(String str){uploadPath = str;} + + /** + * 文档转换器url + */ + public String libreOfficeUrl; + @Value("${libreOffice.url}") + public void setLibreOfficeUrl(String str){libreOfficeUrl = str;} + + /** + * 文档转换器端口 + */ + public int libreOfficePort; + @Value("${libreOffice.port}") + public void setLibreOfficePort(int port){libreOfficePort = port;} + + /** + * 机构Code + */ + /*public String departCode; + @Value("${business.user.depart.code}") + public void setDepartCode(String str){departCode = str;}*/ + + /** + * 学校管理员角色code + */ + /*public String xxRole; + @Value("${business.user.role.xx}") + public void setXxRole(String str){xxRole = str;}*/ + + /** + * 院校管理员角色code + */ + /*public String yxRole; + @Value("${business.user.role.yx}") + public void setYxRole(String str){yxRole = str;}*/ + + /** + * 第一作者角色code + */ + /*public String zzRole; + @Value("${business.user.role.zz}") + public void setZzRole(String str){zzRole = str;}*/ + + /** + * 获去配置文件的指定配置 + * @param key + * @return + */ + public String getConfig(String key){ + return environment.getProperty(key); + } + + /** + * 获取布尔型的配置(从application中获取) + * @param key + * @return + */ + public boolean getBooleanConfig(String key){ + return Boolean.valueOf(getConfig(key)); + } + + /** + * 临时文件存储盘符 + */ + public static String TMP_DICK = "/pdfTemp/"; + + public String getTmpDickPath(){ + return uploadPath + TMP_DICK; + } + + /** + * 临时文件存储盘符 + */ + public static String CONTRACT_DICK = "/contract/"; + + public String getContractDickPath(){ + return uploadPath + CONTRACT_DICK; + } + + /** + * 获取资源路径(可能读不出来) + * @param relativePath 相对路径 + * @return + * @throws FileNotFoundException + */ + @Deprecated + public static String getResourcePath(String relativePath) throws FileNotFoundException { + return ResourceUtil.getResource(ResourceUtils.CLASSPATH_PREFIX + relativePath).getPath(); + } + + /** + * 从jar或是war中读取文件 + * @param relativePath + * @return + * @throws IOException + */ + public InputStream getResourceInputStream(String relativePath) throws IOException { + ClassPathResource classPathResource = new ClassPathResource(relativePath); + return classPathResource.getInputStream(); + } + +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/IdGen.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/IdGen.java new file mode 100644 index 00000000..5df4ef5a --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/IdGen.java @@ -0,0 +1,39 @@ +package org.jeecg.modules.tools; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.RandomUtil; +import org.jeecg.common.util.UUIDGenerator; + +/** + * 生成UUID + */ +public class IdGen { + + /** + * 生成32为ID,例如【9af39bd886d96d100186d96d10e10000】 + * @return + */ + public static String uuid(){ + return UUIDGenerator.generate(); + } + + + /** + * 生成 19为随机字符(数字+字母) +13位毫秒数 = 32位ID + * @return + */ + public static String timeId(){ + Long now = DateUtil.current(true); + return RandomUtil.randomString(19) + now; + } + + public static void main(String[] args) { +// System.out.printf(timeId()); +// for (int i = 0; i < 10000000; i++) { + for (int i = 0; i < 10; i++) { + System.out.println(i+"\t\t"+ timeId()); +// System.out.println(i+"\t\t"+ uuid()); + } + + } +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/URLEncoder.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/URLEncoder.java new file mode 100644 index 00000000..233eba4a --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/URLEncoder.java @@ -0,0 +1,76 @@ +package org.jeecg.modules.tools; + +import java.io.Serializable; +import java.util.BitSet; +import java.util.regex.Pattern; + +public class URLEncoder implements Serializable { + private static final long serialVersionUID = 1L; + public static final URLEncoder DEFAULT = createDefault(); + private final BitSet safeCharacters; + + public static URLEncoder createDefault() { + URLEncoder encoder = new URLEncoder(); + encoder.addSafeCharacter('-'); + encoder.addSafeCharacter('.'); + encoder.addSafeCharacter('_'); + encoder.addSafeCharacter('~'); + encoder.addSafeCharacter('!'); + encoder.addSafeCharacter('$'); + encoder.addSafeCharacter('&'); + encoder.addSafeCharacter('\''); + encoder.addSafeCharacter('('); + encoder.addSafeCharacter(')'); + encoder.addSafeCharacter('*'); + encoder.addSafeCharacter('+'); + encoder.addSafeCharacter(','); + encoder.addSafeCharacter(';'); + encoder.addSafeCharacter('='); + encoder.addSafeCharacter(':'); + encoder.addSafeCharacter('@'); + encoder.addSafeCharacter('/'); + return encoder; + } + + public URLEncoder() { + this(new BitSet(256)); + + char i; + for(i = 'a'; i <= 'z'; ++i) { + this.addSafeCharacter(i); + } + + for(i = 'A'; i <= 'Z'; ++i) { + this.addSafeCharacter(i); + } + + for(i = '0'; i <= '9'; ++i) { + this.addSafeCharacter(i); + } + + } + + private URLEncoder(BitSet safeCharacters) { + this.safeCharacters = safeCharacters; + } + + public void addSafeCharacter(char c) { + this.safeCharacters.set(c); + } + + public String encode(String path) { + StringBuilder rewrittenPath = new StringBuilder(path.length()); + String regexStr = "[\u4E00-\u9FA5]"; + for(int i = 0; i < path.length(); ++i) { + int c = path.charAt(i); + String _char = String.valueOf((char)c); + if (this.safeCharacters.get(c)) { + rewrittenPath.append((char)c); + }else if(Pattern.matches(regexStr, _char)){ + rewrittenPath.append((char)c); + } + + } + return rewrittenPath.toString(); + } +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/excel/DownloadtExcelUtils.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/excel/DownloadtExcelUtils.java new file mode 100644 index 00000000..15fe0cb6 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/excel/DownloadtExcelUtils.java @@ -0,0 +1,85 @@ +package org.jeecg.modules.tools.excel; + +import org.apache.commons.io.IOUtils; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.Workbook; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLEncoder; + +/** + * 下载表格工具 + */ +public class DownloadtExcelUtils { + + public static String genFileExtension(Workbook workbook,String codedFileName){ + if (workbook instanceof HSSFWorkbook) { + codedFileName = codedFileName + ".xls"; + } else { + codedFileName = codedFileName + ".xlsx"; + } + return codedFileName; + } + + /** + * 下载表格, + * @param codedFileName + * @param workbook + * @param request + * @param response + * @throws IOException + */ + public static void download(String codedFileName, Workbook workbook, HttpServletRequest request, HttpServletResponse response) throws IOException { + codedFileName = genFileExtension(workbook,codedFileName); + + if (request.getHeader("USER-AGENT").toLowerCase().indexOf("msie") > 0 || request.getHeader("USER-AGENT").toLowerCase().indexOf("rv:11.0") > 0) { + codedFileName = URLEncoder.encode(codedFileName, "UTF8"); + } else { + codedFileName = new String(codedFileName.getBytes("UTF-8"), "ISO-8859-1"); + } + response.setHeader("content-disposition", "attachment;filename=" + codedFileName); + ServletOutputStream out = response.getOutputStream(); + workbook.write(out); + out.flush(); + } + + /** + * 生成文件 + * @param workbook excel对象 + * @param codedFileName 文件名称,不带扩展名 + * @param toFilePath 生成完整路径 + * @throws IOException + */ + public static void toFile(Workbook workbook,String codedFileName,String toFilePath) throws IOException { + codedFileName = genFileExtension(workbook,codedFileName); + OutputStream out = new FileOutputStream(new File(toFilePath + codedFileName)); + workbook.write(out); + IOUtils.closeQuietly(out); +// FileUtils.createFile(toFilePath + codedFileName); + //FileUtils.copyFile(new File(toFilePath + codedFileName),out); + //out转in +// FileInputStream in = new FileInputStream(out); +// FileUtil.writeFromStream(in,"toFilePath + codedFileName"); + } + + /** + * 生成字节输入流 + * @param workbook excel对象 + * @throws IOException + */ + public static InputStream toByteFile(Workbook workbook) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + workbook.write(out); + InputStream input = new ByteArrayInputStream(out.toByteArray()); + IOUtils.closeQuietly(out); + //创建in输入流 + //根据内存输出流创建内存输入流 + + return input; + } + + +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/excel/ExportExcel.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/excel/ExportExcel.java new file mode 100644 index 00000000..c69abe7b --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/excel/ExportExcel.java @@ -0,0 +1,566 @@ +package org.jeecg.modules.tools.excel; + +import cn.hutool.core.io.IoUtil; +import com.google.common.collect.Lists; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.*; +import org.jeecg.common.util.SpringContextUtils; +import org.jeecg.modules.tools.CommonUtil; +import org.jeecg.modules.tools.Global; +import org.jeecg.modules.tools.word.WordOperator; + +import javax.imageio.ImageIO; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Map; + + +/** + * 导出excel工具类 + * @author binzec + */ +@Slf4j +public class ExportExcel { + + private static Global global = SpringContextUtils.getBean(Global.class); + + public void setGlobal(Global g){ + global = g; + } + + /** + * 模板替换标签开头 + */ + private final static String LABEL_STR = "{"; + /** + * 模板替换标签结尾 + */ + private final static String LABEL_END = "}"; + + /** + * 模板根路径 + */ + private final static String FILE_PATH = "excelTemplates\\"; + + private final static String OFFICE_FILE_PATH = "officetemplates\\"; + + /** + * 获取XSSFWorkbook对象 + * @param templateName word模板名字(必须放在/src/main/resources/file/wordTemplates下) + */ + public static XSSFWorkbook getExcelOperator(String templateName) throws Exception { + InputStream is = null; + XSSFWorkbook wb = null; + try { + //从jar或war中读取资源文件流 + is = global.getResourceInputStream(OFFICE_FILE_PATH + templateName); + //直接从路径中获取 +// is = new FileInputStream(new File(global.uploadPath + "/" + templateName)); + wb = new XSSFWorkbook(is); + + }finally { + IoUtil.close(is); + } + return wb; + } + + /** + * 获取Workbook对象 + * @param templateName word模板名字(必须放在/src/main/resources/file/wordTemplates下) + */ + public static Workbook getExcelOperator2(String templateName) throws Exception { + InputStream is = null; + Workbook workbook = null; + try { + //从jar或war中读取资源文件流 + is = global.getResourceInputStream(OFFICE_FILE_PATH + templateName); + workbook = WorkbookFactory.create(is); + //直接从路径中获取 +// is = new FileInputStream(new File(global.uploadPath + "/" + templateName)); + }finally { + IoUtil.close(is); + } + return workbook; + } + + /** + * 获取Workbook对象 + * @param templateName word模板名字(必须放在/src/main/resources/file/wordTemplates下) + */ + public static InputStream getIs(String templateName) throws Exception { + InputStream is = null; + //从jar或war中读取资源文件流 + is = global.getResourceInputStream(OFFICE_FILE_PATH + templateName); + return is; + } + + /** + * 下载生成的excel文档 + * @param request 请求 + * @param response 响应 + * @param fileName 新文档名字(不带后缀名) + */ + public static void download(HttpServletRequest request, HttpServletResponse response, String fileName, String templateName) throws Exception { + ServletOutputStream os = null; + InputStream is = null; + try { + //从jar或war中读取资源文件流 + is = global.getResourceInputStream(OFFICE_FILE_PATH + templateName); + // 设置好响应头,然后将文件保存到输出流里即可 +// response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); +// response.setHeader("Content-Disposition", "attachment;filename=" + FileUtils.encodeFileName(FileUtils.addRandom(fileName) + ".xlsx", request)); + os = response.getOutputStream(); + //IoUtil.copy(is, os); +// byte[] b = IoUtil.readBytes(is); + + IoUtil.copy(is, os, IoUtil.DEFAULT_BUFFER_SIZE); + } catch (Exception e) { + e.printStackTrace(); + } finally { + IoUtil.close(is); + if(os != null) { + IoUtil.close(os); + } + } + } + + //根据模板创建表格对象 + public static XSSFWorkbook createByTemplate(String templateName) throws IOException { + //直接从路径中获取 + XSSFWorkbook wb = null; + InputStream is = null; + //从jar或war中读取资源文件流 + try{ + is = global.getResourceInputStream(FILE_PATH + templateName); + wb = new XSSFWorkbook(is); + }finally { + IoUtil.close(is); + } + return wb; + } + + /** + * 替换其中的模板文字 + * @param workbook + * @param replaceMap + */ + public static void replaceText(XSSFWorkbook workbook, Map replaceMap){ + //获取每个Sheet表 + for (int i = 0; i < workbook.getNumberOfSheets(); i++) { + log.info("┏ Sheet表{}",i); + XSSFSheet sheet = workbook.getSheetAt(i); + if(sheet != null){ + //获取每行 + for (int j = 0; j <= sheet.getLastRowNum(); j++) { + log.info("┃\t┏ 第{}行",j); + XSSFRow row = sheet.getRow(j); + //中间如果没有数据,则会出现空行,跳过这些空行 + if(row != null){ + //获取每个单元格 + for (int k = 0; k < row.getLastCellNum(); k++) { + XSSFCell cell = row.getCell(k); + //按不同的类型取字符值 + String cellValue = getStringValue(cell); + log.info("┃\t┃\t 第{}单元格\t单元格值为\t{}\t将要替换为\t{}",k,cell,cellValue); + //单元格会存在空的情况, + if(row.getCell(k) != null && StringUtils.isNotBlank(cellValue)){ + //将【{xxx},{yyy}】替换成【x数据,y数据】 + String txt = CommonUtil.getReplaceMapValue(cellValue,replaceMap,LABEL_STR,LABEL_END); + //如果值是一致的,则不再替换,节省资源 + if(!StringUtils.equals(txt,cellValue)){ + log.info("┃\t┃\t 替换成功!"); + row.getCell(k).setCellValue(txt); + } + } + } + } + log.info("┃\t┗ 处理完毕\t共计{}行",sheet.getLastRowNum()); + } + } + log.info("┗ 处理完毕\t共计{}个",workbook.getNumberOfSheets()); + } + } + + /** + * + * @param workbook 工做表 + * @param sheetIndex 第几个sheet页,从0开始 + * @param tempRowIndex 模板row(行x)坐标,从0开始 + * @param tempCellIndex 模板col(列y)坐标,从0开始 + * @param isDeleteTempRow 是否删除模板行 + * @param rowList 数据列表 + */ + public static void insertTable(XSSFWorkbook workbook, int sheetIndex,int tempRowIndex,int tempCellIndex,boolean isDeleteTempRow,List> rowList){ + insertTable(workbook, sheetIndex,tempRowIndex,tempCellIndex,isDeleteTempRow,false,rowList); + } + + /** + * + * @param workbook 工做表 + * @param sheetIndex 第几个sheet页,从0开始 + * @param tempRowIndex 模板row(行x)坐标,从0开始 + * @param tempCellIndex 模板col(列y)坐标,从0开始 + * @param isDeleteTempRow 是否删除模板行 + * @param isForceAddRow 是否强添加新行 + * @param rowList 数据列表 + */ + public static void insertTable(XSSFWorkbook workbook, int sheetIndex,int tempRowIndex,int tempCellIndex,boolean isDeleteTempRow,boolean isForceAddRow,List> rowList){ + //获取sheet + XSSFSheet sheet = workbook.getSheetAt(sheetIndex); + if(sheet == null){ + log.error("sheet页不存在,请检查模板,sheetIndex: {}",sheetIndex); + return; + } + //获取模板行 + XSSFRow tempRow = sheet.getRow(tempRowIndex); + if(tempRow == null){ + log.error("模板row不存在,请检查模板,sheetIndex: {},tempRowIndex: {}",sheetIndex,tempRowIndex); + return; + } + if(rowList.isEmpty()){ + //删除模板行 + if(isDeleteTempRow){ + //删除得偏移行 + sheet.removeRow(tempRow); + if(sheet.getLastRowNum() >= tempRowIndex+1){ + sheet.shiftRows(tempRowIndex+1,sheet.getLastRowNum(),-1); + } + } + log.info("没数据你还搁着寻思啥呢,退出吧!"); + return; + } + + //往单元格里填数据 + for (int di = 0; di < rowList.size(); di++) { + //数据行 + List drow = rowList.get(di); + //获取下一行,在模板行的下一行操作 + XSSFRow nextRow = sheet.getRow(tempRowIndex + di + 1); + if(isForceAddRow){ + nextRow = null; + } + if(nextRow == null){ + //如果是新行,则偏移后面的行 + nextRow = sheet.createRow(tempRowIndex + di + 1); + //如果最后一行比偏移的那一行要小,则不能再偏移会报错的 + if(tempRowIndex + di + 2 <= sheet.getLastRowNum()){ + //偏移的是模板行下的新行的下一行,为了避免底下的表格内容被当前的数据覆盖 + sheet.shiftRows(tempRowIndex + di + 2,sheet.getLastRowNum(),1); + } + } + //具体的单元格部分 + for (int dj = 0; dj < drow.size(); dj++) { + //取单元格 + XSSFCell nextCell = nextRow.getCell(tempCellIndex + dj); + if(nextCell == null){ + nextCell = nextRow.createCell(tempCellIndex + dj); + } + Object o = drow.get(dj); + if (o instanceof String){ + //复制模板行对应单元格的样式 + XSSFCell tempCell = tempRow.getCell(tempCellIndex + dj); + if(tempCell != null){ + //塞入模板单元格的样式 + XSSFCellStyle tempStyle = tempCell.getCellStyle(); + nextCell.setCellStyle(tempStyle); + } + //添加值 + nextCell.setCellValue((String) drow.get(dj)); + }else if(o instanceof XSSFCellStyleAndValue){ + XSSFCellStyleAndValue val = (XSSFCellStyleAndValue) drow.get(dj); + nextCell.setCellStyle(val.getStyle()); + nextCell.setCellValue(val.getValue()); + }else if(o instanceof XSSFCellImage){ + //图片类型的 + XSSFCellImage img = (XSSFCellImage) drow.get(dj); + //short defaultRowHeight = sheet.getDefaultRowHeight(); + //sheet.setDefaultRowHeight((short) 50); + nextRow.setHeight((short) 1000); + XSSFDrawing sxssfDrawing = sheet.createDrawingPatriarch(); + ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream(); + //anchor主要用于设置图片的属性 + XSSFClientAnchor anchor = img.getClientAnchor(); + //自动算出放在哪行哪列 + //img.getWidthCell() + //img.getHeightCell() + anchor.setCol1(nextCell.getColumnIndex());//开始x点 + anchor.setRow1(nextCell.getRowIndex()-1);//开始y点 + anchor.setCol2(nextCell.getColumnIndex()+img.getWidthCell());//结束x点 + anchor.setRow2(nextCell.getRowIndex()-1+img.getWidthCell());//结束y点 + + + anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_DO_RESIZE); + //将图片读到BufferedImage + BufferedImage bufferedImage = null; + try { + bufferedImage = ImageIO.read(new File(img.getImagePath())); + // 将图片写入流中 + ImageIO.write(bufferedImage, img.getImageType(), byteArrayOut); + sxssfDrawing.createPicture(anchor, workbook.addPicture(byteArrayOut.toByteArray(), img.getXSSFWorkbookType())); + //还原默认高度,但不知道能不能用 + //sheet.setDefaultRowHeight(defaultRowHeight); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + //删除模板 + if(isDeleteTempRow){ + //删除得偏移行 + sheet.removeRow(tempRow); + if(sheet.getLastRowNum() >= tempRowIndex+1) { + sheet.shiftRows(tempRowIndex + 1, sheet.getLastRowNum(), -1); + } + } + + } + + @Data + public class Coordinate{ + /** + * sheet页坐标,从0开始 + */ + private int sheetIndex = 0; + /** + * 行坐标,从0开始 + */ + private int rowIndex = 0; + /** + * 单元格列坐标,从0开始 + */ + private int cellIndex = 0; + public Coordinate(){ + super(); + } + + /** + * 默认设置sheet页坐标为0 + * @param rowIndex 行坐标,从0开始 + * @param cellIndex 单元格列坐标,从0开始 + */ + public Coordinate(int rowIndex,int cellIndex){ + super(); + this.rowIndex = rowIndex; + this.cellIndex = cellIndex; + } + + /** + * 开放设置三维的构造 + * @param sheetIndex sheet页坐标,从0开始 + * @param rowIndex 行坐标,从0开始 + * @param cellIndex 单元格列坐标,从0开始 + */ + public Coordinate(int sheetIndex,int rowIndex,int cellIndex){ + super(); + this.sheetIndex = sheetIndex; + this.rowIndex = rowIndex; + this.cellIndex = cellIndex; + } + } + + /** + * + * @param workbook + * @param coordinate + * @return true: 存在| false: 不存在 + */ + public static int checkCell(XSSFWorkbook workbook,Coordinate coordinate){ + //获取起始sheet + XSSFSheet sheet = workbook.getSheetAt(coordinate.getSheetIndex()); + if(sheet == null){ + log.error("sheet不存在,请检查模板,sheetIndex: {}",coordinate.getSheetIndex()); + return -1; + } + XSSFRow row = sheet.getRow(coordinate.getRowIndex()); + if(row == null){ + log.error("row不存在,请检查模板,sheetIndex: {},rowIndex: {}",coordinate.getSheetIndex(),coordinate.getRowIndex()); + return -2; + } + + XSSFCell cell = row.getCell(coordinate.getCellIndex()); + if(cell == null){ + log.error("cell不存在,请检查模板,sheetIndex: {},rowIndex: {},cellIndex: {} ",coordinate.getSheetIndex(),coordinate.getRowIndex(),coordinate.getCellIndex()); + return -3; + } + return 0; + } + + public static void shiftColls(XSSFWorkbook workbook, Coordinate formStart,Coordinate formEnd,Coordinate toStart,Coordinate toEnd){ + //先行校验坐标合法性 + if(formStart == null && checkCell(workbook,formStart) != 0){ + log.error("起始坐标不正确"); + return; + } +// if(formEnd == null && checkCell(workbook,formEnd) != 0){ +// log.error("结束坐标不正确"); +// return; +// } +// if(toStart == null && checkCell(workbook,toStart) != 0){ +// log.error("到某处的起始坐标不正确"); +// return; +// } +// if(toEnd == null && checkCell(workbook,toEnd) != 0){ +// log.error("到某处的结束坐标不正确"); +// return; +// } + + XSSFSheet formStartSheet = workbook.getSheetAt(formStart.getSheetIndex()); + XSSFRow formStartRow = formStartSheet.getRow(formStart.getRowIndex()); + XSSFCell formStartCell = formStartRow.getCell(formStart.getCellIndex()); + +// XSSFSheet formEndSheet = workbook.getSheetAt(formEnd.getSheetIndex()); +// XSSFRow formEndRow = formEndSheet.getRow(formEnd.getRowIndex()); +// XSSFCell formEndCell = formEndRow.getCell(formEnd.getCellIndex()); + + } + + /** + * 计算偏移量,按删除模板行计算 + * @param absoluteRowNumber 相对模板的行号(从0开始计算) + * @param previousAddRowNumber 累计新增的行数,如果是0,则是第一个,不计算删除模板行 + * @return + */ + public static int getDeviationRowNumber(int absoluteRowNumber, int previousAddRowNumber){ + return getDeviationRowNumber(absoluteRowNumber,previousAddRowNumber != 0,previousAddRowNumber); + } + + /** + * + * @param absoluteRowNumber 相对模板的行号(从0开始计算) + * @param isDeleteTempRow 是否删除模板行 + * @param previousAddRowNumber 累计新增的行数 + * @return + */ + public static int getDeviationRowNumber(int absoluteRowNumber,boolean isDeleteTempRow, int previousAddRowNumber){ + return absoluteRowNumber + previousAddRowNumber + (isDeleteTempRow?-1:0); + } + + + + /** + * + * 功能说明 : 补充空列 + * 创建者 : byx + * 修改日期 : 2018年12月25日 + * @param returnList:填充到这个list中 + * @param inputList:输入的list,业务信息列表,用来计算空列的初始值 + * @param endRowNum:总共N行 + * @param colNum:拥有几列 + */ + public static void fillNullCall(List> returnList, List inputList, int endRowNum, int colNum){ + WordOperator.fillNullCall(returnList,inputList,endRowNum,colNum); + } + + /** + * + * 功能说明 : 补充空列 + * 创建者 : byx + * 修改日期 : 2018年12月25日 + * @param returnList:填充到这个list中 + * @param startRowNum:从N开始循环 + * @param endRowNum:总共N行 + * @param colNum:拥有几列 + */ + public static void fillNullCall(List> returnList, int startRowNum, int endRowNum, int colNum){ + WordOperator.fillNullCall(returnList,startRowNum,endRowNum,colNum); + } + + + /** + * 从单元格里取值 + * @param cell + * @return + */ + public static String getStringValue(XSSFCell cell){ + if(cell == null){ return ""; } + String value = ""; + CellType type = cell.getCellTypeEnum(); + switch (type){ + case STRING: + value = cell.getStringCellValue(); + break; + case NUMERIC: + value = String.valueOf(cell.getNumericCellValue()); + break; + case BOOLEAN: + value = String.valueOf(cell.getBooleanCellValue()); + break; + case _NONE: + //内部使用的,, + break; + case BLANK: + //空白单元格 + break; + case ERROR: + //发生错误的单元格 + value = cell.getErrorCellString(); + break; + case FORMULA: + //公式单元格 + value = cell.getCellFormula(); + break; + default: + break; + } + return value; + } + + + + + public static void runTest(HttpServletRequest request, HttpServletResponse response) throws Exception{ + //以模板形式导出 + XSSFWorkbook workbook = ExportExcel.createByTemplate("test.xlsx"); + //指定复制区域,0,1,3 | 0,1,3 指定(复制/剪切)到,1,1,3 | 1,1,3 + //直接偏移,从 0,1,3 | 0,1,3 偏移x,y距离, + //直接偏移,从 0,1,3 | ? 偏移x,y距离, + XSSFSheet sheet = workbook.getSheetAt(0); + XSSFRow row = sheet.getRow(1); + XSSFCell cell = row.getCell(3); + row.createCell(5).copyCellFrom(cell,new CellCopyPolicy()); + row.createCell(3).setCellValue("????什么颜色"); + row.createCell(4).setCellValue("????什么颜色2"); + + + + + + + + + + +// row.copyRowFrom(); +// Map map2 = Maps.newHashMap(); +// map2.put("text1","666666"); +// map2.put("text2","777777"); + //模板替换 + //ExportExcel.replaceText(workbook,map2); + List> dlist = Lists.newArrayList(); + for (int i = 0; i < 20; i++) { + List ddlist = Lists.newArrayList(); + dlist.add(ddlist); + for (int j = 0; j < 7; j++) { + ddlist.add("这是数据!第" + (i+1) + "行,第" + (j+1) + "列"); + } + } + int deviationRow = 0; + log.info("开始创建动态表格1..."); + ExportExcel.insertTable(workbook,0,ExportExcel.getDeviationRowNumber(2,deviationRow),1,true,dlist); + //下载文件 + DownloadtExcelUtils.download("临时文件",workbook,request,response); + } + +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/excel/XSSFCellImage.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/excel/XSSFCellImage.java new file mode 100644 index 00000000..a02d53cb --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/excel/XSSFCellImage.java @@ -0,0 +1,151 @@ +package org.jeecg.modules.tools.excel; + +import lombok.Data; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +import java.util.Locale; + +@Data +public class XSSFCellImage { + private String imagePath;//完整的图片路径 + //图片属性 //new XSSFClientAnchor(0, 0, 255, 255, 0, 0, 1, 1)???? + private XSSFClientAnchor clientAnchor; + //图片类型 + private String imageType; + + private Integer widthCell;//占用几格 + private Integer heightCell;//占用几格 + + public Integer getWidthCell() { + if(widthCell == null) return 0; + return widthCell; + } + + public Integer getHeightCell() { + if(heightCell == null) return 0; + return heightCell; + } + + public String getSuffixName(){ + if(StringUtils.isBlank(imagePath)) return null; + int index = StringUtils.lastIndexOf(imagePath,"."); + if(index != -1){ + return imagePath.substring(index+1); + } + return null; + } + + public String getImageType(){ + if(StringUtils.isBlank(imageType)){ + return "png"; + } + return imageType; + } + //获取图片类型,默认是png + public int getXSSFWorkbookType(){ + if(StringUtils.isBlank(imageType)){ + return XSSFWorkbook.PICTURE_TYPE_PNG; + } + int rType = XSSFWorkbook.PICTURE_TYPE_PNG; + String type = imageType.toLowerCase(Locale.ROOT); + switch (imageType){ + case "png": + rType = XSSFWorkbook.PICTURE_TYPE_PNG; + break; + case "bmp": + rType = XSSFWorkbook.PICTURE_TYPE_BMP; + break; + case "gif": + rType = XSSFWorkbook.PICTURE_TYPE_GIF; + break; + case "dib": + rType = XSSFWorkbook.PICTURE_TYPE_DIB; + break; + case "emf": + rType = XSSFWorkbook.PICTURE_TYPE_EMF; + break; + case "eps": + rType = XSSFWorkbook.PICTURE_TYPE_EPS; + break; + case "jpg": + rType = XSSFWorkbook.PICTURE_TYPE_JPEG; + break; + case "jpeg": + rType = XSSFWorkbook.PICTURE_TYPE_JPEG; + break; + case "pict": + rType = XSSFWorkbook.PICTURE_TYPE_PICT; + break; + case "tiff": + rType = XSSFWorkbook.PICTURE_TYPE_TIFF; + break; + case "wmf": + rType = XSSFWorkbook.PICTURE_TYPE_WMF; + break; + case "wpg": + rType = XSSFWorkbook.PICTURE_TYPE_WPG; + break; + } + return rType; + } + + public XSSFCellImage(){ + super(); + } + + public XSSFCellImage(String imagePath){ + super(); + this.imagePath = imagePath; + this.imageType = getSuffixName(); + this.clientAnchor = new XSSFClientAnchor(0, 0, 255, 255, 0, 0, 1, 1); + + } + /* 0, 0, 255, 255, 0, 0, 1, 1 + dx1 第一个单元格中的x坐标。 + dy1 第一个单元格中的y坐标。 + + dx2 第二个单元格中的x坐标。 + dy2 第二个单元格中的y坐标。 + + col1 第一个单元格的列(基于0)。 + row1 第一个单元格的行(基于0)。 + + col2 第二个单元格的列(基于0)。 + row2 第二个单元格的行(基于0) + * @param dx1 the x coordinate within the first cell. + * @param dy1 the y coordinate within the first cell. + * @param dx2 the x coordinate within the second cell. + * @param dy2 the y coordinate within the second cell. + * @param col1 the column (0 based) of the first cell. + * @param row1 the row (0 based) of the first cell. + * @param col2 the column (0 based) of the second cell. + * @param row2 the row (0 based) of the second cell. + */ + public XSSFCellImage(String imagePath,int widthCell,int heightCell){ + super(); + this.imagePath = imagePath; + this.imageType = getSuffixName(); + this.widthCell = widthCell; + this.heightCell = heightCell; + this.clientAnchor = new XSSFClientAnchor(0, 0, 255, 255, 0, 0,0, 0); + } + public XSSFCellImage(XSSFClientAnchor clientAnchor){ + super(); + this.clientAnchor = clientAnchor; + } + public XSSFCellImage(String imagePath,XSSFClientAnchor clientAnchor){ + super(); + this.imagePath = imagePath; + this.imageType = getSuffixName(); + this.clientAnchor = clientAnchor; + } + public XSSFCellImage(String imagePath,XSSFClientAnchor clientAnchor,String imageType){ + super(); + this.imagePath = imagePath; + this.clientAnchor = clientAnchor; + this.imageType = imageType; + } + +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/excel/XSSFCellStyleAndValue.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/excel/XSSFCellStyleAndValue.java new file mode 100644 index 00000000..d8dbaf67 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/excel/XSSFCellStyleAndValue.java @@ -0,0 +1,38 @@ +package org.jeecg.modules.tools.excel; + +import lombok.Data; +import org.apache.poi.xssf.usermodel.XSSFCellStyle; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +@Data +public class XSSFCellStyleAndValue { + private String value; + private XSSFCellStyle style; + + public XSSFCellStyleAndValue(){ + super(); + } + + public XSSFCellStyleAndValue(String value){ + super(); + this.value = value; + } + + public XSSFCellStyleAndValue(XSSFWorkbook workbook){ + super(); + style = workbook.createCellStyle(); + } + + public XSSFCellStyleAndValue(String value, XSSFWorkbook workbook){ + super(); + this.value = value; + style = workbook.createCellStyle(); + } + + public XSSFCellStyleAndValue(String value, XSSFCellStyle style){ + super(); + this.value = value; + this.style = style; + } + +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/image/ZipImage.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/image/ZipImage.java new file mode 100644 index 00000000..f2f28551 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/image/ZipImage.java @@ -0,0 +1,126 @@ +package org.jeecg.modules.tools.image; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.RuntimeUtil; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 压缩图片 + */ +@Component +@Slf4j +public class ZipImage { + + /** + * 是否开启压缩【true:开启压缩 | false:关闭压缩 】 + */ + @Value("${ffmpeg.compress.enable}") + public boolean enable; + + /** + * 执行命令的核心文件 + */ + @Value("${ffmpeg.compress.executableFile}") + public String executableFile; + + /** + * 压缩比,正整数,数越大压缩比越高 + */ + @Value("${ffmpeg.compress.compressionRatio}") + public String compressionRatio; + + /** + * 日志等级,【 quiet | panic | fatal | error | warning | info | verbose | debug | trace】默认值为 info + */ + @Value("${ffmpeg.compress.loglevel}") + public String loglevel; + + /** + * 是否覆盖输出文件【 y:覆盖 | n:不覆盖 】 + */ + @Value("${ffmpeg.compress.isOverwrite}") + public String isOverwrite; + + /** + * 是否删除源文件【 true:删除源文件 | false:不删除源文件 】 + */ + @Value("${ffmpeg.compress.isDeleteSourceFile}") + public boolean isDeleteSourceFile; + + /** + * 是否删除源文件【 true:删除源文件 | false:不删除源文件 】 + */ + @Value("${ffmpeg.compress.extensionFilter}") + public String[] extensionFilter; + + + + + /** + * + * @return [true: 压缩成功 | false: 压缩失败或是未压缩] + */ + public boolean compress(String inputPath,String outPath){ + if(enable){ + //计算扩展名是否在支持列表中 + String extName = FileUtil.extName(inputPath); + if(!StringUtils.equalsAnyIgnoreCase(extName,extensionFilter)){ + log.info("\n--zipImage--\n拒绝压缩该类型的文件!\n输入文件类型:{}\n输入文件地址:",extName,inputPath); + return false; + } + List command = Lists.newArrayList(); + command.add(executableFile); + command.add("-i"); + command.add(inputPath);//入的文件地址 + command.add("-q"); + command.add(compressionRatio);//压缩比 + command.add(outPath);//出的文件地址 + if(StringUtils.isNotBlank(loglevel)){ + command.add("-loglevel");//如果写了日志级别,则添加参数,不写默认是info + command.add(loglevel); + } + if(StringUtils.isNotBlank(isOverwrite)){ + command.add("-" + isOverwrite);//是否覆盖【 y | n 】 + } + + String[] cmdArr = command.toArray(new String[]{}); + log.debug("命令行参数为:{}",command); + log.info("\n--zipImage--\n开始压缩!\n输入文件地址:{}",inputPath); + Process process = RuntimeUtil.exec(cmdArr); + String res = RuntimeUtil.getResult(process); + log.debug("脚本输出信息:\n{}",res); + //没法判断返回值,信息贼多,且没有个成功标识 + if(FileUtil.exist(outPath)){ + log.info("\n--zipImage--\n压缩成功!\n输出文件地址:{}",outPath); + return true; + }else{ + log.error("\n--zipImage--\n压缩失败!\n输出文件不存在,输出文件地址:{}",outPath); + } +// String error = RuntimeUtil.getErrorResult(process); +// log.error(error); +// process. + + }else{ + log.info("\n--zipImage--\n跳过压缩!\n输入文件地址:{}",inputPath); + } + return false; + } + + public static void main(String[] args) { + ZipImage compressImage = new ZipImage(); + compressImage.enable = true; + compressImage.executableFile = "D:\\ProgramGreeFile\\ffmpeg\\bin\\ffmpeg.exe"; + compressImage.compressionRatio = "50"; + compressImage.loglevel = "error"; + compressImage.isOverwrite = "y"; + + boolean res = compressImage.compress("E:\\111tupian\\戚慧森2_1644453980410.jpg","E:\\111tupian\\戚慧森2_1644453980410_mini.jpg"); + log.debug("最终结果:{}",res); + } +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/pdf/PDFUtil.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/pdf/PDFUtil.java new file mode 100644 index 00000000..f0043d79 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/pdf/PDFUtil.java @@ -0,0 +1,60 @@ +package org.jeecg.modules.tools.pdf; + +import com.artofsolving.jodconverter.DefaultDocumentFormatRegistry; +import com.artofsolving.jodconverter.DocumentConverter; +import com.artofsolving.jodconverter.DocumentFormat; +import com.artofsolving.jodconverter.openoffice.connection.OpenOfficeConnection; +import com.artofsolving.jodconverter.openoffice.connection.SocketOpenOfficeConnection; +import com.artofsolving.jodconverter.openoffice.converter.StreamOpenOfficeDocumentConverter; +import org.jeecg.common.util.SpringContextUtils; +import org.jeecg.modules.tools.Global; + +import java.io.File; +import java.net.ConnectException; + +/** + * PFD工具类 + */ +public class PDFUtil { + + private static Global global = SpringContextUtils.getBean(Global.class); + + public void setGlobal(Global g){ + global = g; + } + + /** + * WORD转PDF方法 + * @param sourceFile 要被转化的word文档路径(如E:\\1.docx) + * @param destFile 转成之后pdf文档存放路径(如E:\\1.pdf) + */ + public static void office2PDF(String sourceFile, String destFile) { + try { + File inputFile = new File(sourceFile); + File outputFile = new File(destFile); + // 获得文件格式(2018年9月25日16:12:35添加,不增加格式,多个文档拼接后转换会报错) + DefaultDocumentFormatRegistry formatReg = new DefaultDocumentFormatRegistry(); + DocumentFormat pdfFormat = formatReg.getFormatByFileExtension("pdf"); + DocumentFormat docFormat = formatReg.getFormatByFileExtension("doc"); + + if (!outputFile.getParentFile().exists()) { + outputFile.getParentFile().mkdirs(); + } + OpenOfficeConnection connection = new SocketOpenOfficeConnection(global.libreOfficeUrl, global.libreOfficePort); +// OpenOfficeConnection connection = new SocketOpenOfficeConnection("192.168.1.227", 8100); + connection.connect(); + // 2018年7月19日修改, 原方法生成pdf会报错 wy +// DocumentConverter converter = new OpenOfficeDocumentConverter(connection); + DocumentConverter converter = new StreamOpenOfficeDocumentConverter(connection); + converter.convert(inputFile,docFormat, outputFile,pdfFormat); + connection.disconnect(); + } catch (ConnectException e) { + e.printStackTrace(); + } + } + +// public static void main(String[] args) { +// PDFUtil.office2PDF("G:\\work\\IDEAWork\\jeecg\\beadhouse\\beadhouse_customer_jeecg_java\\src\\main\\resources\\officetemplates\\jkpg\\HT.docx","F:\\ht4.pdf"); +// } + +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/pdf/WordToPDf.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/pdf/WordToPDf.java new file mode 100644 index 00000000..8b1b28f4 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/pdf/WordToPDf.java @@ -0,0 +1,10 @@ +package org.jeecg.modules.tools.pdf; + +public class WordToPDf { + + public static void main(String[] args) { +// Document doc = new Document(); +// doc.loadFromFile("Z:\\PROJECT_1_CONCLUSION.docx"); +// doc.saveToFile("z:\\out\\PROJECT_1_CONCLUSION.pdf", FileFormat.PDF); + } +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/view/ZipDownloadView.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/view/ZipDownloadView.java new file mode 100644 index 00000000..fb89d2b3 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/view/ZipDownloadView.java @@ -0,0 +1,110 @@ +package org.jeecg.modules.tools.view; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.ZipUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.servlet.view.AbstractView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; + +@Controller("FileDownloadView") +@Slf4j +public class ZipDownloadView extends AbstractView { + private static final String CONTENT_TYPE = "application/x-zip-compressed"; + + /** + * 构造函数,设置为下载 + */ + public ZipDownloadView() { + this.setContentType("application/x-zip-compressed"); + } + + /** + * 是不是IE + * @param request + * @return + */ + public boolean isIE(HttpServletRequest request) { + return request.getHeader("USER-AGENT").toLowerCase().indexOf("msie") > 0 || request.getHeader("USER-AGENT").toLowerCase().indexOf("rv:11.0") > 0; + } + + /** + * 返回的内容 + * @param model + * @param request + * @param response + * @throws Exception + */ + protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { + String codedFileName = "临时文件.zip"; + //如果存在文件名,则使用传进来的文件名 + if (model.containsKey("fileName")) { + codedFileName = model.get("fileName") + ".docx"; + } + + //兼容IE + if (this.isIE(request)) { + codedFileName = URLEncoder.encode(codedFileName, "UTF8"); + } else { + codedFileName = new String(codedFileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1); + } + + response.setHeader("content-disposition", "attachment;filename=" + codedFileName); + OutputStream out = response.getOutputStream(); + //第一种压缩,根据实际文件压缩 + String zipFilePath = (String) model.get("zipFilePath"); + File zip = FileUtil.file(zipFilePath + ".zip"); + if (model.containsKey("zipPath")) { + String zipPath = (String) model.get("zipPath"); + //****************************************生成zip压缩包***********************************************************// + File zipDir = FileUtil.file(zipPath); + //FileUtil.mkdir(zipDir); + FileUtil.touch(zip); + ZipUtil.zip(zip,false, zipDir); + //删除临时文件夹 + // FileUtil.del(zipDir); + //files.forEach(FileUtil::del); + //****************************************生成zip压缩包END********************************************************// + + InputStream is = new FileInputStream(zip); + + // InputStream is = Files.newInputStream(Paths.get(zipFilePath + "zip")); + + IoUtil.write(out,true,IoUtil.readBytes(is)); + IoUtil.close(is); + IoUtil.close(out); + boolean isDelFile = (boolean) model.get("isDelFile"); + if(isDelFile){ + FileUtil.del(zip); + FileUtil.del(zipDir); + } + log.info("打包完毕!准备下载!"); + }else{ + List inList = (List) model.get("inList"); + List nameList = (List) model.get("nameList"); + ZipUtil.zip(zip,nameList.toArray(new String[]{}),inList.toArray(new InputStream[]{})); + + InputStream is = new FileInputStream(zip); + IoUtil.write(out,true,IoUtil.readBytes(is)); + IoUtil.close(is); + IoUtil.close(out); + boolean isDelFile = (boolean) model.get("isDelFile"); + if(isDelFile){ + FileUtil.del(zip); + } + log.info("打包完毕!准备下载!"); + + } + } +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/BaseExport.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/BaseExport.java new file mode 100644 index 00000000..1b6725ce --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/BaseExport.java @@ -0,0 +1,162 @@ +package org.jeecg.modules.tools.word; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.system.vo.DictModel; +import org.jeecg.common.util.DateUtils; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +public class BaseExport { + + public static String formatDate(Date date){ + if(date != null){ + return DateUtils.formatDate(date); + } + return ""; + } + + public static String formatDate(Date date,String pattern){ + if(date != null){ + return DateUtils.formatDate(date,pattern); + } + return ""; + } + + public List getDictList(String dictCode){ + return null; + } + + public Map getTxtList(String txt){ + if(StringUtils.isBlank(txt)){ + txt = ""; + } + //转换,拼接的字典 + Map txtMap = Maps.newHashMap(); + List txtList = Lists.newArrayList(txt.split(",")); + txtList.forEach(x -> txtMap.put(x,x)); + return txtMap; + } + + + /** + * 翻译字典 + * @param txt + * @param dictCode + * @return + */ + public String formatDictTxt(String txt, String dictCode){ + if(StringUtils.isBlank(dictCode)){ + return null; + } + //获取字典列表 + List list = getDictList(dictCode); + + //转换,拼接的字典 + Map txtMap = getTxtList(txt); + //组装字典 + StringBuffer sb = new StringBuffer(); + for(int i=0;i list = getDictList(dictCode); + + //转换,拼接的字典 + Map txtMap = getTxtList(txt); + + //组装字典 + StringBuffer sb = new StringBuffer(); + for(int i=0;i list = getDictList(dictCode); + + //转换,拼接的字典 + Map txtMap = getTxtList(txt); + + //组装字典 + StringBuffer sb = new StringBuffer(); + for(int i=0;i export(Object project) throws Exception{ + return Lists.newArrayList(); + } + + /** + * + * 功能说明 : 文档转换为数组类型 + * 创建者 : byx + * 修改日期 : 2018年9月26日 + * @param project + * @return 数组类型的文档片段 + * @throws Exception + */ + public WordOperator[] exportToArray(Object project) throws Exception{ + List allList = export(project); + return allList.toArray(new WordOperator[0]); + } + +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/ExportWord.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/ExportWord.java new file mode 100644 index 00000000..024a56d4 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/ExportWord.java @@ -0,0 +1,452 @@ +package org.jeecg.modules.tools.word; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import com.google.common.collect.Lists; +import org.apache.commons.io.IOUtils; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.jeecg.common.util.SpringContextUtils; +import org.jeecg.modules.tools.FileUtils; +import org.jeecg.modules.tools.Global; +import org.jeecg.modules.tools.IdGen; +import org.jeecg.modules.tools.pdf.PDFUtil; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * 导出word工具类 + * @author binzec + */ +public class ExportWord { + + private static Global global = SpringContextUtils.getBean(Global.class); + + public void setGlobal(Global g){ + global = g; + } + + /** + * 模板根路径 + */ + private final static String FILE_PATH = "officetemplates\\"; + + public static WordOperator getWordOperator(String templateName) throws Exception { + return getWordOperator(templateName,true); + } + + /** + * 获取WordOperator对象 + * @param templateName word模板名字(必须放在/src/main/resources/file/wordTemplates下) + * @param isResource false时,从上传文件位置取数据 ture是从jar或war中读取资源文件流 + * @return + * @throws Exception + */ + public static WordOperator getWordOperator(String templateName,boolean isResource) throws Exception { + InputStream is = null; + WordOperator wo = null; + try { + //从jar或war中读取资源文件流 + if(isResource){ + is = global.getResourceInputStream(FILE_PATH + templateName); + }else{ + //直接从路径中获取 + is = new FileInputStream(new File(global.uploadPath + "/" + templateName)); + } + wo = getWordOperator(is); + }finally { + IoUtil.close(is); + } + return wo; +// return getWordOperator(new FileInputStream(Global.getResourceFile(FILE_PATH + templateName))); + } + + public static WordOperator getWordOperator(InputStream is) throws Exception { + return new WordOperator(is); + } + + /** + * 下载生成的word文档 + * @param request 请求 + * @param response 响应 + * @param fileName 新文档名字(不带后缀名) + * @param wordOperator 文档操作对象 + */ + public static void download_word(HttpServletRequest request, HttpServletResponse response, String fileName, WordOperator wordOperator) throws Exception { + ServletOutputStream os = null; + try { + // 设置好响应头,然后将文件保存到输出流里即可 + response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); + response.setHeader("Content-Disposition", "attachment;filename=" + FileUtils.encodeFileName(FileUtils.addRandom(fileName) + ".docx", request)); + os = response.getOutputStream(); + wordOperator.write(os); + os.flush(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + // 关闭输出流 + if (os != null) { + os.close(); + } + } + } + + /** + * 下载生成的excel文档 + * @param request 请求 + * @param response 响应 + * @param fileName 新文档名字(不带后缀名) + * @param wb 文档操作对象 + */ + public static void download_excel(HttpServletRequest request, HttpServletResponse response, String fileName, XSSFWorkbook wb) throws Exception { + ServletOutputStream os = null; + try { + // 设置好响应头,然后将文件保存到输出流里即可 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", "attachment;filename=" + FileUtils.encodeFileName(FileUtils.addRandom(fileName) + ".xlsx", request)); + os = response.getOutputStream(); + wb.write(os); + os.flush(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + // 关闭输出流 + if (os != null) { + os.close(); + } + } + } + /** + * 下载生成的excel文档 + * @param request 请求 + * @param response 响应 + * @param fileName 新文档名字(不带后缀名) + * @param workbook 文档操作对象 + */ + public static void download_excel2(HttpServletRequest request, HttpServletResponse response, String fileName, Workbook workbook) throws Exception { + ServletOutputStream os = null; + try { + // 设置好响应头,然后将文件保存到输出流里即可 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", "attachment;filename=" + FileUtils.encodeFileName(FileUtils.addRandom(fileName) + ".xlsx", request)); + os = response.getOutputStream(); + workbook.write(os); + os.flush(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + // 关闭输出流 + if (os != null) { + os.close(); + } + } + } + /** + * 下载生成的excel文档 + * @param request 请求 + * @param response 响应 + * @param fileName 新文档名字(不带后缀名) + * @param is 输入流 + */ + public static void download_by_is(HttpServletRequest request, HttpServletResponse response, String fileName, InputStream is) throws Exception { + ServletOutputStream os = null; + try { + // 设置好响应头,然后将文件保存到输出流里即可 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", "attachment;filename=" + FileUtils.encodeFileName(FileUtils.addRandom(fileName) + ".xlsx", request)); + os = response.getOutputStream(); + IoUtil.copy(is, os); + } catch (Exception e) { + e.printStackTrace(); + } finally { + IoUtil.close(is); + if(os != null) { + IoUtil.close(os); + } + } + } + + /** + * 下载生成的word文档(多文件合并) + * @param request 请求 + * @param response 响应 + * @param fileName 新文档名字(不带后缀名) + * @param woList 文档操作对象集合(合并顺序) + */ + public static void download_word(HttpServletRequest request, HttpServletResponse response, String fileName, WordOperator...woList) throws Exception { + download_word(request,response,fileName,".docx",woList); + } + + /** + * 下载生成的word文档(多文件合并) + * @param request 请求 + * @param response 响应 + * @param fileName 新文档名字(不带后缀名) + * @param suffix 新文档的后缀名 + * @param woList 文档操作对象集合(合并顺序) + */ + public static void download_word(HttpServletRequest request, HttpServletResponse response, String fileName, String suffix, WordOperator...woList) throws Exception { + ServletOutputStream os = null; + List tempDocList = new ArrayList<>(); + try { + // 设置响应头 + response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); + response.setHeader("Content-Disposition", "attachment;filename=" + FileUtils.encodeFileName(FileUtils.addRandom(fileName) + suffix, request)); + os = response.getOutputStream(); + // 一个ow对应一份文档,循环保存下来 + for (WordOperator wo : woList) { + String tempDoc = global.getTmpDickPath() + IdGen.uuid() + suffix; + FileUtil.touch(tempDoc); + FileOutputStream fo = new FileOutputStream(tempDoc); + wo.write(fo); + IoUtil.close(fo); + tempDocList.add(tempDoc); + } + // 然后把上面保存的文档合并起来 + String tempDoc = global.getTmpDickPath() + mergeDocx(tempDocList); + tempDocList.add(tempDoc); + // 将这份文档以流的形式保存到输出流中 + IOUtils.copy(new FileInputStream(new File(tempDoc)), os); + os.flush(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + // 关闭输出流 + if (os != null) { + os.close(); + } + // 将全部临时文档删除掉 + for (String tempDoc : tempDocList) { + FileUtils.delFile(tempDoc); + } + } + } + + /** + * 下载生成的pdf文档 + * @param request 请求 + * @param response 响应 + * @param fileName 新文档名字(不带后缀名) + * @param wordOperator 文档操作对象 + */ + public static void download_pdf(HttpServletRequest request, HttpServletResponse response, String fileName, WordOperator wordOperator) throws Exception { + ServletOutputStream os = null; + String tempDoc = null; + String tempPdf = null; + try { + // 设置响应头 + response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); + response.setContentType(MimeUtils.getByExtension("pdf") + ";charset=UTF-8"); + response.setHeader("Content-Disposition", "attachment;filename=" + FileUtils.encodeFileName(FileUtils.addRandom(fileName) + ".pdf", request)); + // 先将生成的word文档保存起来 + tempDoc = global.getTmpDickPath() + IdGen.uuid() + ".docx"; + FileUtil.touch(tempDoc); + FileOutputStream fo = new FileOutputStream(tempDoc); + wordOperator.write(fo); + IoUtil.close(fo); + + // 然后将word转成pdf保存起来 + tempPdf = global.getTmpDickPath() + IdGen.uuid() + ".pdf"; + PDFUtil.office2PDF(tempDoc,tempPdf); +// FilePreviewUtils.word2ToPdf(tempDoc,tempPdf); +// PDFUtils.office2PDF(tempDoc, tempPdf); + // 将这份文档以流的形式保存到输出流中 + os = response.getOutputStream(); + FileUtils.copyFile(new File(tempPdf), os); + os.flush(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + // 关闭输出流 + if (os != null) { + os.close(); + } + // 将临时文档删除掉 + if (tempDoc != null) { + FileUtils.delFile(tempDoc); + } + // 将临时文档删除掉 + if (tempPdf != null) { + FileUtils.delFile(tempPdf); + } + } + } + + /** + * 下载生成的pdf文档(多文件合并) + * @param request 请求 + * @param response 响应 + * @param fileName 新文档名字(不带后缀名) + * @param woList 文档操作对象集合(合并顺序) + */ + public static void download_pdf(HttpServletRequest request, HttpServletResponse response, String fileName, WordOperator... woList) throws Exception { + List tempDocList = new ArrayList(); + ServletOutputStream os = null; + try { + // 设置响应头 + response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); + response.setContentType(MimeUtils.getByExtension("pdf") + ";charset=UTF-8"); + response.setHeader("Content-Disposition", "attachment;filename=" + FileUtils.encodeFileName(FileUtils.addRandom(fileName) + ".pdf", request)); + // 一个ow对应一份文档,循环保存下来 + for (WordOperator ow : woList) { + String tempDoc = global.getTmpDickPath() + IdGen.uuid() + ".docx"; + FileUtil.touch(tempDoc); + FileOutputStream fo = new FileOutputStream(tempDoc); + ow.write(fo); + IoUtil.close(fo); + tempDocList.add(tempDoc); + } + // 然后把上面保存的文档合并起来 + String tempDoc = global.getTmpDickPath() + mergeDocx(tempDocList); + tempDocList.add(tempDoc); + // 再把这份合并好的文档转成pdf + String tempPdf = global.getTmpDickPath() + IdGen.uuid() + ".pdf"; + PDFUtil.office2PDF(tempDoc,tempPdf); +// FilePreviewUtils.word2ToPdf(tempDoc,tempPdf); + tempDocList.add(tempPdf); + // 将这份文档以流的形式保存到输出流中 + os = response.getOutputStream(); + FileUtils.copyFile(new File(tempPdf), os); + os.flush(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + // 关闭输出流 + if (os != null) { + os.close(); + } + // 将全部临时文档删除掉 + for (String tempDoc : tempDocList) { + FileUtils.delFile(tempDoc); + } + } + } + + /** + * 预览生成的pdf文档 + * @param request 请求 + * @param response 响应 + * @param fileName 新文档名字(不带后缀名) + * @param wordOperator 文档操作对象 + * @throws Exception + */ + public static void preview_pdf(HttpServletRequest request, HttpServletResponse response, String fileName, WordOperator wordOperator) throws Exception { + String tempDoc = null; + String tempPdf = null; + ServletOutputStream os = null; + try { +// FileUtils.createDirectory(global.getTmpDickPath()); + // 设置响应头 + response.setContentType(MimeUtils.getByExtension("pdf") + ";charset=UTF-8"); + response.setHeader("Content-Disposition", "inline;filename=" + FileUtils.encodeFileName(FileUtils.addRandom(fileName) + ".pdf", request)); + // 先将生成的word文档保存起来 + // 2018年7月19日修改, 原方法读取word文件会报错 wy +// tempDoc = global.getTmpDickPath() + IdGen.uuid() + ".docx"; + tempDoc = global.getTmpDickPath() + IdGen.uuid() + ".docx"; + FileUtil.touch(tempDoc); + FileOutputStream fo = new FileOutputStream(tempDoc); + wordOperator.write(fo); + IoUtil.close(fo); +// tempDoc = global.getTmpDickPath() + IdGen.uuid() + ".docx"; +// wordOperator.write(new FileOutputStream(tempDoc)); + // 然后将word转成pdf保存起来 + tempPdf = global.getTmpDickPath() + IdGen.uuid() + ".pdf"; + PDFUtil.office2PDF(tempDoc,tempPdf); +// FilePreviewUtils.word2ToPdf(tempDoc,tempPdf); +// PDFUtils.office2PDF(tempDoc, tempPdf); + // 将pdf文档以流的形式输送给response + os = response.getOutputStream(); + //是否将文件地址返回 +// if(returnHeadFile){ +// response.addHeader("Access-Control-Expose-Headers","pdfPath"); +// response.addHeader("Access-Control-Expose-Headers","wordPath"); +// response.setHeader("pdfPath",tempPdf); +// response.setHeader("wordPath",tempDoc); +// } + FileUtils.copyFile(new File(tempPdf), os); + +// File pdfFile = new File(tempPdf); +// FileUtils.copyFile(pdfFile, os); + os.flush(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + // 关闭输出流 + if (os != null) { + os.close(); + } +// if(fileDelete){ + // 将word和pdf两个临时文件删除掉 + if (tempDoc != null) { + FileUtils.delFile(tempDoc); + } + if (tempPdf != null) { + FileUtils.delFile(tempPdf); + } +// } + } + } + + /** + * 预览生成的pdf文档(多文件合并) + * @param request 请求 + * @param response 响应 + * @param fileName 新文档名字(不带后缀名) + * @param woList 文档操作对象集合(合并顺序) + */ + public static void preview_pdf(HttpServletRequest request, HttpServletResponse response, String fileName, WordOperator... woList) throws Exception { + List tempDocList = Lists.newArrayList(); + ServletOutputStream os = null; + try { + // 设置响应头 + response.setContentType(MimeUtils.getByExtension("pdf") + ";charset=UTF-8"); + response.setHeader("Content-Disposition", "inline; filename=" + FileUtils.encodeFileName(FileUtils.addRandom(fileName) + ".pdf", request)); + // 一个ow对应一份文档,循环保存下来 + for (WordOperator ow : woList) { + String tempDoc = global.getTmpDickPath() + IdGen.uuid() + ".docx"; + FileUtil.touch(tempDoc); + FileOutputStream fo = new FileOutputStream(tempDoc); + ow.write(fo); + IoUtil.close(fo); + tempDocList.add(tempDoc); + } + // 然后把上面保存的文档合并起来 + String tempDoc = global.getTmpDickPath() + mergeDocx(tempDocList); + tempDocList.add(tempDoc); + // 再把这份合并好的文档转成pdf + String tempPdf = global.getTmpDickPath() + IdGen.uuid() + ".pdf"; + PDFUtil.office2PDF(tempDoc,tempPdf); +// FilePreviewUtils.word2ToPdf(tempDoc,tempPdf); +// PDFUtils.office2PDF(tempDoc, tempPdf); + tempDocList.add(tempPdf); + // 将这份文档以流的形式保存到输出流中 + os = response.getOutputStream(); + FileUtils.copyFile(new File(tempPdf), os); + os.flush(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + // 关闭输出流 + if (os != null) { + os.close(); + } + // 将全部临时文档删除掉 + for (String tempDoc : tempDocList) { + FileUtils.delFile(tempDoc); + } + } + } + + public static String mergeDocx(List fileNames) throws Exception { + return MergeDoc.mergeDocx(fileNames,global.getTmpDickPath()); + } + +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/MergeDoc.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/MergeDoc.java new file mode 100644 index 00000000..b838fd20 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/MergeDoc.java @@ -0,0 +1,158 @@ +/********************************************************************** +* $Id: MergeDoc.java MergeDoc ,v0.1 2019年4月26日 下午7:12:00 byx Exp $ +* Copyright ©2019 ccqnsoft . All rights reserved +***********************************************************************/ + +package org.jeecg.modules.tools.word; + +import cn.hutool.core.io.IoUtil; +import com.google.common.collect.Lists; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.xwpf.usermodel.BreakType; +import org.apache.poi.xwpf.usermodel.Document; +import org.apache.poi.xwpf.usermodel.XWPFDocument; +import org.apache.poi.xwpf.usermodel.XWPFPictureData; +import org.apache.xmlbeans.XmlOptions; +import org.jeecg.modules.tools.IdGen; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author: Max + * + * @Date: 2018/6/8 + * + * @name: 多个word文件合并,采用poi实现,兼容图片的迁移 + * + * @Description: + */ +public class MergeDoc { + + /** + * 合并若干个文档(按顺序合并),默认的方法,带分页 + * @param fileNames 需要合并的几个word文档全路径 + * @param tempPath 临时文件放在哪 + * @return 返回合并好的文档名称,位置在DICK下 + */ + public static String mergeDocx(List fileNames,String tempPath) throws Exception { + return mergeDocx(fileNames,tempPath,true); + } + + /** + * 合并若干个文档(按顺序合并) + * @param fileNames 需要合并的几个word文档全路径 + * @param tempPath 临时文件放在哪 + * @param isAddBreak 是否自动分页,true:带分页,false:不带分页 + * @return 返回合并好的文档名称,位置在DICK下 + */ + public static String mergeDocx(List fileNames,String tempPath,boolean isAddBreak) throws Exception { + String tmpFileName = IdGen.uuid() + ".docx"; + OutputStream dest = new FileOutputStream(tempPath + tmpFileName); + ArrayList documentList = new ArrayList<>(); + + XWPFDocument doc = null; + for (int i = 0; i < fileNames.size(); i++) { + FileInputStream in = new FileInputStream(fileNames.get(i)); + OPCPackage open = OPCPackage.open(in); + XWPFDocument document = new XWPFDocument(open); + documentList.add(document); + } + + for (int i = 0; i < documentList.size(); i++) { + doc = documentList.get(0); + if(i == 0){//首页直接分页,不再插入首页文档内容 + if(isAddBreak){ + System.out.println("第【" + i + "】表格!"); + documentList.get(i).createParagraph().createRun().addBreak(BreakType.PAGE); + } + }else if(i == documentList.size()-1){//尾页不再分页,直接插入最后文档内容 + System.out.println("第【" + i + "】表格!"); + appendBody(doc,documentList.get(i)); + }else{ + System.out.println("第【" + i + "】表格!"); + if(isAddBreak) { + documentList.get(i).createParagraph().createRun().addBreak(BreakType.PAGE); + } + appendBody(doc,documentList.get(i)); + } + } + doc.write(dest); + IoUtil.close(dest); + System.out.println("*****合并文档成功********"); + return tmpFileName; + } + + public static void appendBody(XWPFDocument src, XWPFDocument append) throws Exception { + CTBody src1Body = src.getDocument().getBody(); + CTBody src2Body = append.getDocument().getBody(); + + List allPictures = append.getAllPictures(); + // 记录图片合并前及合并后的ID + Map map = new HashMap<>(); + for (XWPFPictureData picture : allPictures) { + String before = append.getRelationId(picture); + //将原文档中的图片加入到目标文档中 + String after = src.addPictureData(picture.getData(), Document.PICTURE_TYPE_PNG); + map.put(before, after); + } + appendBody(src1Body, src2Body,map); + } + + private static void appendBody(CTBody src, CTBody append,Map map) throws Exception { + XmlOptions optionsOuter = new XmlOptions(); + optionsOuter.setSaveOuter(); + String appendString = append.xmlText(optionsOuter); + + + String srcString = src.xmlText(); + String rgex = "<[\\s]*?w:sectPr[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?w:sectPr[\\s]*?>"; + appendString = appendString.replaceAll(rgex, ""); + String prefix = srcString.substring(0,srcString.indexOf(">")+1); + String mainPart = srcString.substring(srcString.indexOf(">")+1,srcString.lastIndexOf("<")); + String sufix = srcString.substring( srcString.lastIndexOf("<") ); + String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<")); + if (map != null && !map.isEmpty()) { + //对xml字符串中图片ID进行替换 + for (Map.Entry set : map.entrySet()) { + addPart = addPart.replace(set.getKey(), set.getValue()); + } + } + //将两个文档的xml内容进行拼接 + CTBody makeBody = CTBody.Factory.parse(prefix+mainPart+addPart+sufix); + src.set(makeBody); + } + + //获取文档的xml内容 + public static String getWordXml(File file) throws Exception { + FileInputStream in = new FileInputStream(file); + OPCPackage open = OPCPackage.open(in); + XWPFDocument document = new XWPFDocument(open); + CTBody body = document.getDocument().getBody(); + return body.xmlText(); + } + + public static void main(String[] args) { + String file0 = "D:/opt/upFiles/pdfTemp/yb/0.docx"; + String file1 = "D:/opt/upFiles/pdfTemp/yb/3.docx"; + List files = Lists.newArrayList(); + files.add(file0); + files.add(file1); + try { + MergeDoc.mergeDocx(files,"D:/opt/upFiles/pdfTemp/hb",false); + System.out.println("0 =>" + getWordXml(new File(file0))); + System.out.println("1 =>" + getWordXml(new File(file1))); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + +} \ No newline at end of file diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/MimeUtils.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/MimeUtils.java new file mode 100644 index 00000000..fdfbbb8f --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/MimeUtils.java @@ -0,0 +1,365 @@ +package org.jeecg.modules.tools.word; + +import java.util.HashMap; +import java.util.Map; + +/** + * 响应头文件类型集合 + * @author binzec + */ +public final class MimeUtils { + + /** + * KV对,K-mimeType,V-extension + */ + private static final Map mimeTypeToExtensionMap = new HashMap(); + /** + * KV对,K-extension,V-mimeType + */ + private static final Map extensionToMimeTypeMap = new HashMap(); + + /** + * 添加 + */ + public static void add(String mimeType, String extension) { + if (!mimeTypeToExtensionMap.containsKey(mimeType)) { + mimeTypeToExtensionMap.put(mimeType, extension); + } + extensionToMimeTypeMap.put(extension, mimeType); + } + + /** + * 根据extension获取mimeType + */ + public static String getByExtension(String extension) { + if (extension == null || extension.isEmpty()) { + return null; + } + return extensionToMimeTypeMap.get(extension); + } + + /** + * 根据mimeType获取extension + */ + public static String getByMimeType(String mimeType) { + if (mimeType == null || mimeType.isEmpty()) { + return null; + } + return mimeTypeToExtensionMap.get(mimeType); + } + + /** + * mimeType重复的,默认加载第一个 + */ + static { + add("application/andrew-inset", "ez"); + add("application/dsptype", "tsp"); + add("application/json", "json"); + add("application/futuresplash", "spl"); + add("application/hta", "hta"); + add("application/mac-binhex40", "hqx"); + add("application/mac-compactpro", "cpt"); + add("application/mathematica", "nb"); + add("application/msaccess", "mdb"); + add("application/oda", "oda"); + add("application/ogg", "ogg"); + add("application/pdf", "pdf"); + add("application/pgp-keys", "key"); + add("application/pgp-signature", "pgp"); + add("application/pics-rules", "prf"); + add("application/rar", "rar"); + add("application/rdf+xml", "rdf"); + add("application/rss+xml", "rss"); + add("application/zip", "zip"); + add("application/vnd.android.package-archive", "apk"); + add("application/vnd.cinderella", "cdy"); + add("application/vnd.ms-pki.stl", "stl"); + add("application/vnd.oasis.opendocument.database", "odb"); + add("application/vnd.oasis.opendocument.formula", "odf"); + add("application/vnd.oasis.opendocument.graphics", "odg"); + add("application/vnd.oasis.opendocument.graphics-template", "otg"); + add("application/vnd.oasis.opendocument.image", "odi"); + add("application/vnd.oasis.opendocument.spreadsheet", "ods"); + add("application/vnd.oasis.opendocument.spreadsheet-template", "ots"); + add("application/vnd.oasis.opendocument.text", "odt"); + add("application/vnd.oasis.opendocument.text-master", "odm"); + add("application/vnd.oasis.opendocument.text-template", "ott"); + add("application/vnd.oasis.opendocument.text-web", "oth"); + add("application/vnd.google-earth.kml+xml", "kml"); + add("application/vnd.google-earth.kmz", "kmz"); + add("application/msword", "doc"); + add("application/msword", "dot"); + add("application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docx"); + add("application/vnd.openxmlformats-officedocument.wordprocessingml.template", "dotx"); + add("application/vnd.ms-excel", "xls"); + add("application/vnd.ms-excel", "xlt"); + add("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx"); + add("application/vnd.openxmlformats-officedocument.spreadsheetml.template", "xltx"); + add("application/vnd.ms-powerpoint", "ppt"); + add("application/vnd.ms-powerpoint", "pot"); + add("application/vnd.ms-powerpoint", "pps"); + add("application/vnd.openxmlformats-officedocument.presentationml.presentation", "pptx"); + add("application/vnd.openxmlformats-officedocument.presentationml.template", "potx"); + add("application/vnd.openxmlformats-officedocument.presentationml.slideshow", "ppsx"); + add("application/vnd.rim.cod", "cod"); + add("application/vnd.smaf", "mmf"); + add("application/vnd.stardivision.calc", "sdc"); + add("application/vnd.stardivision.draw", "sda"); + add("application/vnd.stardivision.impress", "sdd"); + add("application/vnd.stardivision.impress", "sdp"); + add("application/vnd.stardivision.math", "smf"); + add("application/vnd.stardivision.writer", "sdw"); + add("application/vnd.stardivision.writer", "vor"); + add("application/vnd.stardivision.writer-global", "sgl"); + add("application/vnd.sun.xml.calc", "sxc"); + add("application/vnd.sun.xml.calc.template", "stc"); + add("application/vnd.sun.xml.draw", "sxd"); + add("application/vnd.sun.xml.draw.template", "std"); + add("application/vnd.sun.xml.impress", "sxi"); + add("application/vnd.sun.xml.impress.template", "sti"); + add("application/vnd.sun.xml.math", "sxm"); + add("application/vnd.sun.xml.writer", "sxw"); + add("application/vnd.sun.xml.writer.global", "sxg"); + add("application/vnd.sun.xml.writer.template", "stw"); + add("application/vnd.visio", "vsd"); + add("application/x-abiword", "abw"); + add("application/x-apple-diskimage", "dmg"); + add("application/x-bcpio", "bcpio"); + add("application/x-bittorrent", "torrent"); + add("application/x-cdf", "cdf"); + add("application/x-cdlink", "vcd"); + add("application/x-chess-pgn", "pgn"); + add("application/x-cpio", "cpio"); + add("application/x-debian-package", "deb"); + add("application/x-debian-package", "udeb"); + add("application/x-director", "dcr"); + add("application/x-director", "dir"); + add("application/x-director", "dxr"); + add("application/x-dms", "dms"); + add("application/x-doom", "wad"); + add("application/x-dvi", "dvi"); + add("application/x-flac", "flac"); + add("application/x-font", "pfa"); + add("application/x-font", "pfb"); + add("application/x-font", "gsf"); + add("application/x-font", "pcf"); + add("application/x-font", "pcf.Z"); + add("application/x-freemind", "mm"); + add("application/x-futuresplash", "spl"); + add("application/x-gnumeric", "gnumeric"); + add("application/x-go-sgf", "sgf"); + add("application/x-graphing-calculator", "gcf"); + add("application/x-gtar", "gtar"); + add("application/x-gtar", "tgz"); + add("application/x-gtar", "taz"); + add("application/x-hdf", "hdf"); + add("application/x-ica", "ica"); + add("application/x-internet-signup", "ins"); + add("application/x-internet-signup", "isp"); + add("application/x-iphone", "iii"); + add("application/x-iso9660-image", "iso"); + add("application/x-jmol", "jmz"); + add("application/x-kchart", "chrt"); + add("application/x-killustrator", "kil"); + add("application/x-koan", "skp"); + add("application/x-koan", "skd"); + add("application/x-koan", "skt"); + add("application/x-koan", "skm"); + add("application/x-kpresenter", "kpr"); + add("application/x-kpresenter", "kpt"); + add("application/x-kspread", "ksp"); + add("application/x-kword", "kwd"); + add("application/x-kword", "kwt"); + add("application/x-latex", "latex"); + add("application/x-lha", "lha"); + add("application/x-lzh", "lzh"); + add("application/x-lzx", "lzx"); + add("application/x-maker", "frm"); + add("application/x-maker", "maker"); + add("application/x-maker", "frame"); + add("application/x-maker", "fb"); + add("application/x-maker", "book"); + add("application/x-maker", "fbdoc"); + add("application/x-mif", "mif"); + add("application/x-ms-wmd", "wmd"); + add("application/x-ms-wmz", "wmz"); + add("application/x-msi", "msi"); + add("application/x-ns-proxy-autoconfig", "pac"); + add("application/x-nwc", "nwc"); + add("application/x-object", "o"); + add("application/x-oz-application", "oza"); + add("application/x-pkcs12", "p12"); + add("application/x-pkcs12", "pfx"); + add("application/x-pkcs7-certreqresp", "p7r"); + add("application/x-pkcs7-crl", "crl"); + add("application/x-quicktimeplayer", "qtl"); + add("application/x-shar", "shar"); + add("application/x-shockwave-flash", "swf"); + add("application/x-stuffit", "sit"); + add("application/x-sv4cpio", "sv4cpio"); + add("application/x-sv4crc", "sv4crc"); + add("application/x-tar", "tar"); + add("application/x-texinfo", "texinfo"); + add("application/x-texinfo", "texi"); + add("application/x-troff", "t"); + add("application/x-troff", "roff"); + add("application/x-troff-man", "man"); + add("application/x-ustar", "ustar"); + add("application/x-wais-source", "src"); + add("application/x-wingz", "wz"); + add("application/x-webarchive", "webarchive"); + add("application/x-webarchive-xml", "webarchivexml"); + add("application/x-x509-ca-cert", "crt"); + add("application/x-x509-user-cert", "crt"); + add("application/x-xcf", "xcf"); + add("application/x-xfig", "fig"); + add("application/xhtml+xml", "xhtml"); + add("audio/3gpp", "3gpp"); + add("audio/amr", "amr"); + add("audio/basic", "snd"); + add("audio/midi", "mid"); + add("audio/midi", "midi"); + add("audio/midi", "kar"); + add("audio/midi", "xmf"); + add("audio/mobile-xmf", "mxmf"); + add("audio/mpeg", "mpga"); + add("audio/mpeg", "mpega"); + add("audio/mpeg", "mp2"); + add("audio/mpeg", "mp3"); + add("audio/mpeg", "m4a"); + add("audio/mpegurl", "m3u"); + add("audio/prs.sid", "sid"); + add("audio/x-aiff", "aif"); + add("audio/x-aiff", "aiff"); + add("audio/x-aiff", "aifc"); + add("audio/x-gsm", "gsm"); + add("audio/x-mpegurl", "m3u"); + add("audio/x-ms-wma", "wma"); + add("audio/x-ms-wax", "wax"); + add("audio/x-pn-realaudio", "ra"); + add("audio/x-pn-realaudio", "rm"); + add("audio/x-pn-realaudio", "ram"); + add("audio/x-realaudio", "ra"); + add("audio/x-scpls", "pls"); + add("audio/x-sd2", "sd2"); + add("audio/x-wav", "wav"); + add("image/bmp", "bmp"); + add("image/gif", "gif"); + add("image/ico", "cur"); + add("image/ico", "ico"); + add("image/ief", "ief"); + add("image/jpeg", "jpeg"); + add("image/jpeg", "jpg"); + add("image/jpeg", "jpe"); + add("image/pcx", "pcx"); + add("image/png", "png"); + add("image/svg+xml", "svg"); + add("image/svg+xml", "svgz"); + add("image/tiff", "tiff"); + add("image/tiff", "tif"); + add("image/vnd.djvu", "djvu"); + add("image/vnd.djvu", "djv"); + add("image/vnd.wap.wbmp", "wbmp"); + add("image/x-cmu-raster", "ras"); + add("image/x-coreldraw", "cdr"); + add("image/x-coreldrawpattern", "pat"); + add("image/x-coreldrawtemplate", "cdt"); + add("image/x-corelphotopaint", "cpt"); + add("image/x-icon", "ico"); + add("image/x-jg", "art"); + add("image/x-jng", "jng"); + add("image/x-ms-bmp", "bmp"); + add("image/x-photoshop", "psd"); + add("image/x-portable-anymap", "pnm"); + add("image/x-portable-bitmap", "pbm"); + add("image/x-portable-graymap", "pgm"); + add("image/x-portable-pixmap", "ppm"); + add("image/x-rgb", "rgb"); + add("image/x-xbitmap", "xbm"); + add("image/x-xpixmap", "xpm"); + add("image/x-xwindowdump", "xwd"); + add("model/iges", "igs"); + add("model/iges", "iges"); + add("model/mesh", "msh"); + add("model/mesh", "mesh"); + add("model/mesh", "silo"); + add("text/calendar", "ics"); + add("text/calendar", "icz"); + add("text/comma-separated-values", "csv"); + add("text/css", "css"); + add("text/html", "htm"); + add("text/html", "html"); + add("text/h323", "323"); + add("text/iuls", "uls"); + add("text/mathml", "mml"); + add("text/plain", "txt"); + add("text/plain", "asc"); + add("text/plain", "text"); + add("text/plain", "diff"); + add("text/plain", "po"); + add("text/richtext", "rtx"); + add("text/rtf", "rtf"); + add("text/texmacs", "ts"); + add("text/text", "phps"); + add("text/tab-separated-values", "tsv"); + add("text/xml", "xml"); + add("text/x-bibtex", "bib"); + add("text/x-boo", "boo"); + add("text/x-c++hdr", "h++"); + add("text/x-c++hdr", "hpp"); + add("text/x-c++hdr", "hxx"); + add("text/x-c++hdr", "hh"); + add("text/x-c++src", "c++"); + add("text/x-c++src", "cpp"); + add("text/x-c++src", "cxx"); + add("text/x-chdr", "h"); + add("text/x-component", "htc"); + add("text/x-csh", "csh"); + add("text/x-csrc", "c"); + add("text/x-dsrc", "d"); + add("text/x-haskell", "hs"); + add("text/x-java", "java"); + add("text/x-literate-haskell", "lhs"); + add("text/x-moc", "moc"); + add("text/x-pascal", "p"); + add("text/x-pascal", "pas"); + add("text/x-pcs-gcd", "gcd"); + add("text/x-setext", "etx"); + add("text/x-tcl", "tcl"); + add("text/x-tex", "tex"); + add("text/x-tex", "ltx"); + add("text/x-tex", "sty"); + add("text/x-tex", "cls"); + add("text/x-vcalendar", "vcs"); + add("text/x-vcard", "vcf"); + add("video/3gpp", "3gpp"); + add("video/3gpp", "3gp"); + add("video/3gpp", "3g2"); + add("video/dl", "dl"); + add("video/dv", "dif"); + add("video/dv", "dv"); + add("video/fli", "fli"); + add("video/m4v", "m4v"); + add("video/mpeg", "mpeg"); + add("video/mpeg", "mpg"); + add("video/mpeg", "mpe"); + add("video/mp4", "mp4"); + add("video/mpeg", "VOB"); + add("video/quicktime", "qt"); + add("video/quicktime", "mov"); + add("video/vnd.mpegurl", "mxu"); + add("video/x-la-asf", "lsf"); + add("video/x-la-asf", "lsx"); + add("video/x-mng", "mng"); + add("video/x-ms-asf", "asf"); + add("video/x-ms-asf", "asx"); + add("video/x-ms-wm", "wm"); + add("video/x-ms-wmv", "wmv"); + add("video/x-ms-wmx", "wmx"); + add("video/x-ms-wvx", "wvx"); + add("video/x-msvideo", "avi"); + add("video/x-sgi-movie", "movie"); + add("x-conference/x-cooltalk", "ice"); + add("x-epoc/x-sisx-app", "sisx"); + } +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordImageLabel.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordImageLabel.java new file mode 100644 index 00000000..c55e5b35 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordImageLabel.java @@ -0,0 +1,115 @@ +/********************************************************************** +* $Id: WordImageLabel.java WordImageLabel ,v0.1 2018年9月19日 下午8:50:48 byx Exp $ +* Copyright ©2018 sida . All rights reserved +***********************************************************************/ + +package org.jeecg.modules.tools.word; + + +import org.apache.commons.lang3.StringUtils; + +/** +* 功能说明:从标签中读取键值,宽高和后缀内容 +* 创建者:byx +* 创建时间:2018年9月19日 +*
+* 修改时间:       修改者:            
+* 修改内容:
+* 
+*/ +public class WordImageLabel { + + public static final String SPLIT_STR = ","; + + private String key = ""; //键值 + + private int width = 0; //宽 + + private int height = 0; //高 + + private String picAttch = ""; //后缀值 + + public WordImageLabel(Object obj){ + if(obj instanceof String){ + this.wordImageLabel((String)obj); + } + } + + /** + * + * 功能说明 : 如:‘image,10,10,后缀’ + * 创建者 : byx + * 修改日期 : 2018年9月19日 + * @param value + */ + public WordImageLabel(String value){ + super(); + this.wordImageLabel(value); + } + + /** + * + * 功能说明 : 如:‘image,10,10,后缀’ + * 创建者 : byx + * 修改日期 : 2018年9月19日 + * @param value + */ + public void wordImageLabel(String value) { + if(StringUtils.isNotBlank(value)){ + String[] strList = StringUtils.split(value, SPLIT_STR); + if(strList.length == 0){ + key = value; + }else if(strList.length == 1){ + key = strList[0]; + }else if(strList.length == 2){ + key = strList[0]; + width = Integer.valueOf(strList[1]); + }else if(strList.length == 3){ + key = strList[0]; + width = Integer.valueOf(strList[1]); + height = Integer.valueOf(strList[2]); + }else if(strList.length == 4){ + key = strList[0]; + width = Integer.valueOf(strList[1]); + height = Integer.valueOf(strList[2]); + picAttch = strList[3]; + } + } + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } + + public String getPicAttch() { + return picAttch; + } + + public void setPicAttch(String picAttch) { + this.picAttch = picAttch; + } + + + + +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordOperator.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordOperator.java new file mode 100644 index 00000000..c51a74f3 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordOperator.java @@ -0,0 +1,1154 @@ +package org.jeecg.modules.tools.word; + +import cn.hutool.core.io.IoUtil; +import com.google.common.collect.Lists; +import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.util.Units; +import org.apache.poi.xwpf.usermodel.*; +import org.jeecg.common.util.SpringContextHolder; +import org.jeecg.modules.tools.CommonUtil; +import org.jeecg.modules.tools.FileUtils; +import org.jeecg.modules.tools.Global; +import org.jeecg.modules.tools.IdGen; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 对word模板的操作,包含替换文本和列表向下扩展两种
+ * 本工具类的使用需要配合ExportWord.class,具体Demo看ExportWord类即可 + * @author byx + */ +public class WordOperator { + + private static Logger logger = LoggerFactory.getLogger(WordOperator.class); + + /** + * 模板替换标签开头 + */ + private final static String LABEL_STR = "{"; + /** + * 模板替换标签结尾 + */ + private final static String LABEL_END = "}"; + /** + * 模板替换图片标签开头(List) + */ + private final static String LABEL_IMAGE_STR = "<"; + /** + * 模板替换图片标签结尾(List) + */ + private final static String LABEL_IMAGE_END = ">"; + + /** + * 模板替换图片标签开头(List) + */ + private final static String LABEL_RICH_TEXT_STR = " + * 因为微软只提供XWPFRun对象替换文本,而它在切割段落文本的时候却是非常没有规律的,故而本方法要求模板必须遵循一定的规范来编写:
+ * 1.模板里除了标签外,其余地方禁止使用"{"和"}"字符串;
+ * 2.不允许出现"{}"这种写法,即标签必须包含key; + */ + public WordOperator(InputStream is) throws Exception { + doc = new XWPFDocument(is); + } + + + + /** + * 要替换标签的KV对 + */ + private Map replaceMap; + + public Map getReplaceMap() { + return replaceMap; + } + + public void setReplaceMap(Map replaceMap) { + this.replaceMap = replaceMap; + } + + /** + * + * 功能说明 : 创建图片,根据段落 + * 创建者 : byx + * 修改日期 : 2018年9月15日 + * @param pic 图片位置 + * @param picPath 图片路径 + * @param width 图片宽度 + * @param height 图片高度 + * @param picAttch 图片后缀文字 + */ + public void createPicture(XWPFParagraph pic, String picPath, int width, int height, String picAttch){ + pic.setAlignment(ParagraphAlignment.CENTER); + + XWPFRun run = pic.createRun(); + + try { + if(StringUtils.isBlank(picPath) || picPath.lastIndexOf(".") == -1){ + logger.error("文件路径错误:"); + logger.error(picPath); + return; + } + //根据文件后缀名设置图片格式 + String picType = picPath.substring(picPath.lastIndexOf("\\.")+1); + int res = XWPFDocument.PICTURE_TYPE_PICT; + if(picType != null){ + if(picType.equalsIgnoreCase("png")){ + res = XWPFDocument.PICTURE_TYPE_PNG; + }else if(picType.equalsIgnoreCase("gif")) { + res = XWPFDocument.PICTURE_TYPE_GIF; + }else if(picType.equalsIgnoreCase("emf")){ + res = XWPFDocument.PICTURE_TYPE_EMF; + }else if(picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")){ + res = XWPFDocument.PICTURE_TYPE_JPEG; + }else if(picType.equalsIgnoreCase("wmf")){ + res = XWPFDocument.PICTURE_TYPE_WMF; + }else if(picType.equalsIgnoreCase("pict")){ + res = XWPFDocument.PICTURE_TYPE_PICT; + }else if(picType.equalsIgnoreCase("dib")){ + res = XWPFDocument.PICTURE_TYPE_DIB; + }else if(picType.equalsIgnoreCase("tiff")){ + res = XWPFDocument.PICTURE_TYPE_TIFF; + }else if(picType.equalsIgnoreCase("eps")){ + res = XWPFDocument.PICTURE_TYPE_EPS; + }else if(picType.equalsIgnoreCase("bmp")){ + res = XWPFDocument.PICTURE_TYPE_BMP; + }else if(picType.equalsIgnoreCase("wpg")){ + res = XWPFDocument.PICTURE_TYPE_WPG; + } + } + //塞进文档中 +// doc.addPictureData(new FileInputStream(picPath),res); + if(StringUtils.isNotBlank(picAttch)){ + run.setText(picAttch); + } + //补充完整路径 + Global global = SpringContextHolder.getBean(Global.class); + InputStream is = new FileInputStream(global.uploadPath + "/" + picPath); +// run.addPicture(is,res,picPath,Units.pixelToEMU(width), Units.pixelToEMU(height)); + run.addPicture(is,res,picPath,Units.toEMU(width), Units.toEMU(height)); +// run.addPicture(is,res,picPath,width, height); + IoUtil.close(is); + } catch (InvalidFormatException e) { + logger.error(e.toString()); + e.printStackTrace(); + } catch (IOException e) { + logger.error("没有找到相应的文件!"); + logger.error(e.toString()); + e.printStackTrace(); + } + } + + /** + * + * 功能说明 : 多个图片支持(|分隔) + * 创建者 : byx + * 修改日期 : 2018年9月26日 + * @param pic 图片位置 + * @param picPath 图片路径 + * @param width 图片宽度 + * @param height 图片高度 + * @param picAttch 图片后缀文字 + */ + public void createPicturePlus(XWPFParagraph pic, String picPath, int width, int height, String picAttch){ + if(StringUtils.isBlank(picPath)){ + logger.warn("文件路径错误:"); + logger.warn(picPath); + return; + } + if(StringUtils.indexOf(picPath, ",") == -1){ + //为单个时直接插入图片 + createPicture(pic,picPath,width,height,picAttch); + }else{ + //多个时, + String[] paths = StringUtils.split(picPath,","); + for (String s : paths) { + createPicture(pic,s,width,height,picAttch); + } + } +// createPicture + } + + /** + * 在word模板文档里,用{key}标签来表示要替换的内容,把key对应的value存储于replaceMap里,即可完成替换 + */ + public WordOperator replaceText(Map replaceMap) { + this.replaceMap = replaceMap; + // 替换文本内容的标签 + List paragraphList = doc.getParagraphs(); + replaceText(paragraphList); + // 替换表格内部标签 + List tableList = doc.getTables(); + for (XWPFTable table : tableList) { + for (int i = 0; i < table.getNumberOfRows(); i++) { + XWPFTableRow row = table.getRow(i); + List tableCellList = row.getTableCells(); + for (XWPFTableCell cell : tableCellList) { + replaceText(cell.getParagraphs()); + } + } + } + return this; + } + + /** + * 在word模板文档里,用{key}标签来表示要替换的内容,把key对应的value存储于replaceMap里,即可完成替换(带有替换图片的功能) + */ + public WordOperator replaceTextPlus(Map replaceMap) { + this.replaceMap = replaceMap; + // 替换文本内容的标签 + List paragraphList = doc.getParagraphs(); + replaceTextPlus(paragraphList); + // 替换表格内部标签 + List tableList = doc.getTables(); + for (XWPFTable table : tableList) { + for (int i = 0; i < table.getNumberOfRows(); i++) { + XWPFTableRow row = table.getRow(i); + List tableCellList = row.getTableCells(); + for (XWPFTableCell cell : tableCellList) { + replaceTextPlus(cell.getParagraphs()); + } + } + } + return this; + } + + + public WordOperator replaceTextPlusRemove(Map replaceMap,String removeList) { + this.replaceMap = replaceMap; + // 替换文本内容的标签 + List paragraphList = doc.getParagraphs(); + replaceTextPlus(paragraphList); + // 替换表格内部标签 + List tableList = doc.getTables(); + for (XWPFTable table : tableList) { + for (int i = 0; i < table.getNumberOfRows(); i++) { + XWPFTableRow row = table.getRow(i); + List tableCellList = row.getTableCells(); + for (XWPFTableCell cell : tableCellList) { + replaceTextPlus(cell.getParagraphs()); + } + } + } + return this; + } + + /** + * 在word模板文档里,用{key}标签来表示要替换的内容,把key对应的value存储于replaceMap里,即可完成替换(带图片,仅支持一个图片) + */ + public WordOperator replaceTextAndCreatePicture(Map replaceMap) { + this.replaceMap = replaceMap; + // 替换文本内容的标签 + List paragraphList = doc.getParagraphs(); + replaceText(paragraphList); + // 替换表格内部标签 + List tableList = doc.getTables(); + for (XWPFTable table : tableList) { + for (int i = 0; i < table.getNumberOfRows(); i++) { + XWPFTableRow row = table.getRow(i); + List tableCellList = row.getTableCells(); + for (XWPFTableCell cell : tableCellList) { + replaceTextAndCreatePicture(cell.getParagraphs()); + } + } + } + return this; + } + + /** + * 把替换段落抽取出一个方法,在替换文本和替换表格里都可以调用 + */ + private void replaceText(List paragraphList) { + // 取出word模板里的全部段落,遍历 + for (int i = 0; i < paragraphList.size(); i++) { + XWPFParagraph paragraph = paragraphList.get(i); + // 拿出每一个段落,判断内容里是否包含字符串"{"和"}",只有两者同时存在了才执行替换标签的逻辑 + String paragraphText = paragraph.getText(); + if (!StringUtils.isBlank(paragraphText) && paragraphText.indexOf(LABEL_STR) != -1 && paragraphText.indexOf(LABEL_END) != -1) { + // 每一个段落分很多小段文本,官方API只能否通过XWPFRun对象执行替换文本功能 + List runList = paragraph.getRuns(); + // 组装replaceMap的key + String key = ""; + // 是否检测到有"{"字符串,有为true,没有为false + boolean include = false; + for (int j = 0; j < runList.size(); j++) { + XWPFRun run = runList.get(j); + String text = run.getText(0); + // 取出每一个小段里标签头和尾的角标 + int labelStrIndex = StringUtils.indexOf(text, LABEL_STR); + int labelEndIndex = StringUtils.indexOf(text, LABEL_END); + // 只有{ + if (labelStrIndex != -1 && labelEndIndex == -1) { + // 例如有"aaa{bbb",则aaa不动,{bbb去掉,同时把bbb累加到key里 + String textBefore = text.substring(0, labelStrIndex); + String textAfter = text.substring(labelStrIndex + 1); + run.setText(textBefore, 0); + key += textAfter; + include = true; + // 只有} + } else if (labelStrIndex == -1 && labelEndIndex != -1) { + // 例如有"aaa}bbb",则aaa}去掉,bbb不动,同时把aaa累加到key里 + String textBefore = text.substring(0, labelEndIndex); + String textAfter = text.substring(labelEndIndex + 1); + key += textBefore; + Object value = replaceMap.get(key); + if (value == null || StringUtils.isBlank(value.toString())) { + value = ""; + } + key = ""; + run.setText(value + textAfter, 0); + include = false; + // 两个都没有 + } else if (labelStrIndex == -1 && labelEndIndex == -1) { + // 例如有"aaa",如果之前有{了,则把内容累加到key里,同时去掉内容;如果没有{,则说明是普通文本,跳过不管 + if (include) { + key += text; + run.setText("", 0); + } + // 两个都存在,这种情况比较复杂。经过多次试验,XWPFRun切割内容,只会同时各出现1次而已 + } else { + // {在前,}在后 + if (labelStrIndex < labelEndIndex) { + // 例如有"aaa{bbb}ccc",则aaa不动,{bbb}去掉同时进行替换,ccc保留(因为有规范,所以这是一个独立完整的标签,且bbb绝对有值) + String textBefore = text.substring(0, labelStrIndex); + String textMiddle = text.substring(labelStrIndex + 1, labelEndIndex); + String textAfter = text.substring(labelEndIndex + 1); + Object value = replaceMap.get(textMiddle); + if (value == null || StringUtils.isBlank(value.toString())) { + value = ""; + } + run.setText(textBefore + value + textAfter, 0); + key = ""; + include = false; + // }在前,{在后 + } else { + // 例如有"aaa}bbb{ccc",则aaa}去掉,且进行替换;bbb不动,{ccc去掉,累加到key里去 + String textBefore = text.substring(0, labelEndIndex); + String textMiddle = text.substring(labelEndIndex + 1, labelStrIndex); + String textAfter = text.substring(labelStrIndex + 1); + key += textBefore; + Object value = replaceMap.get(key); + if (value == null || StringUtils.isBlank(value.toString())) { + value = ""; + } + run.setText(value + textMiddle, 0); + key = textAfter; + include = true; + } + } + } + } + } + } + /** + * 把替换段落抽取出一个方法,在替换文本和替换表格里都可以调用(可以替换为图片) + */ + private void replaceTextPlus(List paragraphList) { + //InputStream imageFileIs = null; + // 取出word模板里的全部段落,遍历 + for (int i = 0; i < paragraphList.size(); i++) { + XWPFParagraph paragraph = paragraphList.get(i); + // 拿出每一个段落,判断内容里是否包含字符串"{"和"}",只有两者同时存在了才执行替换标签的逻辑 + String paragraphText = paragraph.getText(); + if (!StringUtils.isBlank(paragraphText) && StringUtils.indexOf(paragraphText,LABEL_STR) != -1 && StringUtils.indexOf(paragraphText,LABEL_END) != -1) { + // 每一个段落分很多小段文本,官方API只能通过XWPFRun对象执行替换文本功能 + List runList = paragraph.getRuns(); + // 组装replaceMap的key + String key = ""; + // 是否检测到有"{"字符串,有为true,没有为false + boolean include = false; + for (int j = 0; j < runList.size(); j++) { + XWPFRun run = runList.get(j); + //颜色 +// run.setColor("F3C917"); + //加粗 +// run.setBold(true); + //背景色 +// run.getCTR().addNewRPr().addNewHighlight().setVal(STHighlightColor.YELLOW); + //底纹 +// CTShd shd = run.getCTR().addNewRPr().addNewShd(); +// shd.setFill("FFFF00"); + + String text = run.getText(0); + // 取出每一个小段里标签头和尾的角标 + //是否存在两个{ + boolean isParagraphReplaceAllText = false; + if(StringUtils.ordinalIndexOf(text,LABEL_STR,2) != -1){ + isParagraphReplaceAllText = true; + text = paragraphReplaceAllText(text,replaceMap); + } + int labelStrIndex = StringUtils.indexOf(text, LABEL_STR); + int labelEndIndex = StringUtils.indexOf(text, LABEL_END); + // 只有{ + if (labelStrIndex != -1 && labelEndIndex == -1) { + // 例如有"aaa{bbb",则aaa不动,{bbb去掉,同时把bbb累加到key里 + String textBefore = text.substring(0, labelStrIndex); + String textAfter = text.substring(labelStrIndex + 1); + run.setText(textBefore, 0); + key += textAfter; + include = true; + // 只有} + } else if (labelStrIndex == -1 && labelEndIndex != -1) { + // 例如有"aaa}bbb",则aaa}去掉,bbb不动,同时把aaa累加到key里 + String textBefore = text.substring(0, labelEndIndex); + String textAfter = text.substring(labelEndIndex + 1); + key += textBefore; + //Object value = replaceMap.get(key); + String value = (String)replaceMap.get(key); + if (StringUtils.isBlank(value)) value = ""; + WordTextStyle style = (WordTextStyle)replaceMap.get(key + "_style"); + //添加样式 + if(style != null) style.run(run); + if (StringUtils.isBlank(value)) value = ""; + value = StringEscapeUtils.unescapeHtml4(value); + key = ""; + //--2020-04修改 + String[] values = value.split("\r\n"); + if(values.length > 1) { + run.setText(values[0],0); + for (int x = 1; x < values.length; x++) { + //存在分段则新建一个run + XWPFRun newrun = paragraph.createRun(); + //copy样式 + newrun.getCTR().setRPr(run.getCTR().getRPr()); + //换行 + newrun.addBreak(); + //缩进 + //newrun.addTab(); + if(x == values.length - 1) { + newrun.setText(values[x] + textAfter); + }else { + newrun.setText(values[x]); + } + } + }else { + run.setText(value + textAfter, 0); + } + include = false; + // 两个都没有 + } else if (labelStrIndex == -1 && labelEndIndex == -1) { + // 例如有"aaa",如果之前有{了,则把内容累加到key里,同时去掉内容;如果没有{,则说明是普通文本,跳过不管 + if (include) { + key += text; + run.setText("", 0); + } + if(isParagraphReplaceAllText){ + //替换文本 + run.setText(text,0); + } + // 两个都存在,这种情况比较复杂。经过多次试验,XWPFRun切割内容,只会同时各出现1次而已 + } else { + // {在前,}在后 + if (labelStrIndex < labelEndIndex) { + // 例如有"aaa{bbb}ccc",则aaa不动,{bbb}去掉同时进行替换,ccc保留(因为有规范,所以这是一个独立完整的标签,且bbb绝对有值) + String textBefore = text.substring(0, labelStrIndex); + String textMiddle = text.substring(labelStrIndex + 1, labelEndIndex); + String textAfter = text.substring(labelEndIndex + 1); + String value = (String)replaceMap.get(textMiddle); + if (StringUtils.isBlank(value)) value = ""; + WordTextStyle style = (WordTextStyle)replaceMap.get(textMiddle + "_style"); + //添加样式 + if(style != null) style.run(run); + value = StringEscapeUtils.unescapeHtml4(value); + //--2020-04修改 + String[] values = value.split("\r\n"); + if(values.length > 1) { + run.setText(textBefore + values[0],0); + for (int x = 1; x < values.length; x++) { + //存在分段则新建一个run + XWPFRun newrun = paragraph.createRun(); + //copy样式 + newrun.getCTR().setRPr(run.getCTR().getRPr()); + //换行 + newrun.addBreak(); + //缩进 + //newrun.addTab(); + if(x == values.length - 1) { + newrun.setText(values[x] + textAfter,0); + }else { + newrun.setText(values[x]); + } + } + }else { + run.setText(textBefore + value + textAfter, 0); + } + + key = ""; + include = false; + // }在前,{在后 + } else { + // 例如有"aaa}bbb{ccc",则aaa}去掉,且进行替换;bbb不动,{ccc去掉,累加到key里去 + String textBefore = text.substring(0, labelEndIndex); + String textMiddle = text.substring(labelEndIndex + 1, labelStrIndex); + String textAfter = text.substring(labelStrIndex + 1); + key += textBefore; + String value = (String)replaceMap.get(key); + if (value == null || StringUtils.isBlank(value.toString())) value = ""; + WordTextStyle style = (WordTextStyle)replaceMap.get(textMiddle + "_style"); + //添加样式 + if(style != null) style.run(run); + value = StringEscapeUtils.unescapeHtml4(value); + //--2020-04修改 + String[] values = value.toString().split("\r\n"); + if(values.length > 1) { + run.setText(values[0],0); + for (int x = 1; x < values.length; x++) { + //存在分段则新建一个run + XWPFRun newrun = paragraph.createRun(); + //copy样式 + newrun.getCTR().setRPr(run.getCTR().getRPr()); + //换行 + newrun.addBreak(); + //缩进 + //newrun.addTab(); + if(x == values.length - 1) { + newrun.setText(values[x] + textMiddle,0); + }else { + newrun.setText(values[x]); + } + } + }else { + run.setText(value + textMiddle, 0); + } + key = textAfter; + include = true; + } + } + } + //富文本标签(转成图片的富文本) + } else if(StringUtils.isNotBlank(paragraphText) && paragraphText.indexOf(LABEL_RICH_TEXT_STR) != -1 && paragraphText.indexOf(LABEL_RICH_TEXT_END) != -1){ + System.out.println("paragraphText="+paragraphText); + //如果匹配到‘<’和‘>’则开始替换为图片 + // 每一个段落分很多小段文本,官方API只能否通过XWPFRun对象执行替换文本功能 + List runList = paragraph.getRuns(); + //组装replaceMap的key + String key = ""; + + // + //摘出key + int $labelStrIndex = StringUtils.indexOf(paragraphText, LABEL_RICH_TEXT_STR); + int $labelEndIndex = StringUtils.indexOf(paragraphText, LABEL_RICH_TEXT_END); + + //必须存在开始和结束标签 + if($labelStrIndex != -1 && $labelEndIndex != -1){ + key = paragraphText; + key = key.substring($labelStrIndex + LABEL_RICH_TEXT_STR.length()); + $labelEndIndex = StringUtils.indexOf(key, LABEL_RICH_TEXT_END); + key = key.substring(0,$labelEndIndex); + } + + //去除整行(整个paragraph) + for (int j = 0; j < runList.size(); j++) { + XWPFRun run = runList.get(j); + run.setText("", 0); + } + //添加图片 + //创建一个run + //XWPFRun $run = paragraph.createRun(); + Object value = replaceMap.get(key); + if (value == null || StringUtils.isBlank(value.toString())) { + value = ""; + } + String imagePath = getHtmlImage(value.toString()); + //try { + //imageFileIs = new FileInputStream(new File(imagePath)); + //$run.addPicture(imageFileIs, 6, "image", 595,842); + //} catch (InvalidFormatException | IOException e) { + //logger.error("图片读取或添加错误!"); + //logger.error(e.getMessage(),e); + //e.printStackTrace(); + //} + createPicturePlus(paragraph,imagePath,595,842,""); + //FileUtils.deleteFile(imagePath); + //$run.setText(); + //图片标签 + }else if(StringUtils.isNotBlank(paragraphText) && paragraphText.indexOf(LABEL_IMAGE_STR) != -1 && paragraphText.indexOf(LABEL_IMAGE_END) != -1){ + //如果匹配到‘<’和‘>’则开始替换为图片 + // 每一个段落分很多小段文本,官方API只能否通过XWPFRun对象执行替换文本功能 + List runList = paragraph.getRuns(); + // 组装replaceMap的key + String key = ""; + //读取简易标签对象如:‘image,10,10,后缀’ + WordImageLabel wordImageLabel = null; + // 是否检测到有"<"字符串,有为true,没有为false + boolean include = false; + for (int j = 0; j < runList.size(); j++) { + XWPFRun run = runList.get(j); + String text = run.getText(0); + // 取出每一个小段里标签头和尾的角标 + int labelStrIndex = StringUtils.indexOf(text, LABEL_IMAGE_STR); + int labelEndIndex = StringUtils.indexOf(text, LABEL_IMAGE_END); + // 只有< + if (labelStrIndex != -1 && labelEndIndex == -1) { + // 例如有"aaabbb",则aaa>去掉,bbb不动,同时把aaa累加到key里 + String textBefore = text.substring(0, labelEndIndex); +// String textAfter = text.substring(labelEndIndex + 1); + key += textBefore; + //读取简易标签对象 + wordImageLabel = new WordImageLabel(key); + Object value = replaceMap.get(wordImageLabel.getKey()); + if (value == null || StringUtils.isBlank(value.toString())) { + value = ""; + } + key = ""; + //创建图片 + run.setText("", 0); +// run.setText(value + textAfter, 0); + createPicturePlus(paragraph,(String)value,wordImageLabel.getWidth(),wordImageLabel.getHeight(),wordImageLabel.getPicAttch()); + include = false; + // 两个都没有 + } else if (labelStrIndex == -1 && labelEndIndex == -1) { + // 例如有"aaa",如果之前有<了,则把内容累加到key里,同时去掉内容;如果没有<,则说明是普通文本,跳过不管 + if (include) { + key += text; + run.setText("", 0); + } + // 两个都存在,这种情况比较复杂。经过多次试验,XWPFRun切割内容,只会同时各出现1次而已 + } else { + // <在前,>在后 + if (labelStrIndex < labelEndIndex) { + // 例如有"aaaccc",则aaa不动,去掉同时进行替换,ccc保留(因为有规范,所以这是一个独立完整的标签,且bbb绝对有值) +// String textBefore = text.substring(0, labelStrIndex); + String textMiddle = text.substring(labelStrIndex + 1, labelEndIndex); +// String textAfter = text.substring(labelEndIndex + 1); + wordImageLabel = new WordImageLabel(textMiddle); + Object value = replaceMap.get(wordImageLabel.getKey()); + if (value == null || StringUtils.isBlank(value.toString())) { + value = ""; + } + run.setText("", 0); +// run.setText(textBefore + value + textAfter, 0); + createPicturePlus(paragraph,(String)value,wordImageLabel.getWidth(),wordImageLabel.getHeight(),wordImageLabel.getPicAttch()); + key = ""; + include = false; + // >在前,<在后 + } else { + // 例如有"aaa>bbb去掉,且进行替换;bbb不动, replaceMap){ + return CommonUtil.getReplaceMapValue(str,replaceMap,LABEL_STR,LABEL_END); + } + + /** + * 把替换段落抽取出一个方法,在替换文本和替换表格里都可以调用(带图片),只替换{IMAGE} + */ + private void replaceTextAndCreatePicture(List paragraphList) { + // 取出word模板里的全部段落,遍历 + for (int i = 0; i < paragraphList.size(); i++) { + XWPFParagraph paragraph = paragraphList.get(i); + // 拿出每一个段落,判断内容里是否包含字符串"{"和"}",只有两者同时存在了才执行替换标签的逻辑 + String paragraphText = paragraph.getText(); + if (!StringUtils.isBlank(paragraphText)) { + //判断是否为唯一的image关键字 + if(StringUtils.equals(paragraphText, "{IMAGE}") && paragraphText.indexOf(LABEL_STR) != -1 && paragraphText.indexOf(LABEL_END) != -1){ + //去掉关键字标记 + List runList = paragraph.getRuns(); + for (int j = 0; j < runList.size(); j++) { + XWPFRun run = runList.get(j); + run.setText("", 0); + } + //获取图片地址 + String picPath = (String) replaceMap.get("IMAGE"); + //创建图片 + createPicture(paragraph,picPath,83,83," "); + //创建后不需要普通的文字替换了,跳过本段落 + continue; + } + // 每一个段落分很多小段文本,官方API只能否通过XWPFRun对象执行替换文本功能 + List runList = paragraph.getRuns(); + // 组装replaceMap的key + String key = ""; + // 是否检测到有"{"字符串,有为true,没有为false + boolean include = false; + for (int j = 0; j < runList.size(); j++) { + XWPFRun run = runList.get(j); + String text = run.getText(0); + // 取出每一个小段里标签头和尾的角标 + int labelStrIndex = text.indexOf(LABEL_STR); + int labelEndIndex = text.indexOf(LABEL_END); + // 只有{ + if (labelStrIndex != -1 && labelEndIndex == -1) { + // 例如有"aaa{bbb",则aaa不动,{bbb去掉,同时把bbb累加到key里 + String textBefore = text.substring(0, labelStrIndex); + String textAfter = text.substring(labelStrIndex + 1); + run.setText(textBefore, 0); + //createPicture(paragraph,""); + key += textAfter; + include = true; + // 只有} + } else if (labelStrIndex == -1 && labelEndIndex != -1) { + // 例如有"aaa}bbb",则aaa}去掉,bbb不动,同时把aaa累加到key里 + String textBefore = text.substring(0, labelEndIndex); + String textAfter = text.substring(labelEndIndex + 1); + key += textBefore; + Object value = replaceMap.get(key); + if (value == null || StringUtils.isBlank(value.toString())) { + value = ""; + } + key = ""; + run.setText(value + textAfter, 0); + include = false; + // 两个都没有 + } else if (labelStrIndex == -1 && labelEndIndex == -1) { + // 例如有"aaa",如果之前有{了,则把内容累加到key里,同时去掉内容;如果没有{,则说明是普通文本,跳过不管 + if (include) { + key += text; + run.setText("", 0); + } + // 两个都存在,这种情况比较复杂。经过多次试验,XWPFRun切割内容,只会同时各出现1次而已 + } else { + // {在前,}在后 + if (labelStrIndex < labelEndIndex) { + // 例如有"aaa{bbb}ccc",则aaa不动,{bbb}去掉同时进行替换,ccc保留(因为有规范,所以这是一个独立完整的标签,且bbb绝对有值) + String textBefore = text.substring(0, labelStrIndex); + String textMiddle = text.substring(labelStrIndex + 1, labelEndIndex); + String textAfter = text.substring(labelEndIndex + 1); + Object value = replaceMap.get(textMiddle); + if (value == null || StringUtils.isBlank(value.toString())) { + value = ""; + } + run.setText(textBefore + value + textAfter, 0); + key = ""; + include = false; + // }在前,{在后 + } else { + // 例如有"aaa}bbb{ccc",则aaa}去掉,且进行替换;bbb不动,{ccc去掉,累加到key里去 + String textBefore = text.substring(0, labelEndIndex); + String textMiddle = text.substring(labelEndIndex + 1, labelStrIndex); + String textAfter = text.substring(labelStrIndex + 1); + key += textBefore; + Object value = replaceMap.get(key); + if (value == null || StringUtils.isBlank(value.toString())) { + value = ""; + } + run.setText(value + textMiddle, 0); + key = textAfter; + include = true; + } + } + } + } + } + } + +// public static void main(String[] args) { +// String text = ""; +// //摘出key +// int $labelStrIndex = StringUtils.indexOf(text, LABEL_RICH_TEXT_STR); +// int $labelEndIndex = StringUtils.indexOf(text, LABEL_RICH_TEXT_END); +// +// +// System.out.println("$labelStrIndex=" + $labelStrIndex); +// System.out.println("$labelEndIndex=" + $labelEndIndex); +// //必须存在开始和结束标签 +// if($labelStrIndex != -1 && $labelEndIndex != -1){ +// String key = text; +// key = key.substring($labelStrIndex + LABEL_RICH_TEXT_STR.length()); +// $labelEndIndex = StringUtils.indexOf(key, LABEL_RICH_TEXT_END); +// System.out.println("key="+key); +// key = key.substring(0,$labelEndIndex); +// System.out.println("key="+key); +// +// } +// +// HtmlImageGenerator imageGenerator = new HtmlImageGenerator(); +//// String htmlstr = "

编号:10001

"; +// String htmlstr = "

编号:10001

"; +//// String htmlstr = ""; +//// htmlstr = htmlstr.replaceAll("/education/", "http://127.0.0.1:9000/education/"); +// imageGenerator.loadHtml(htmlstr); +//// imageGenerator.loadUrl("https://www.baidu.com"); +//// imageGenerator.setSize(new Dimension(595,842)); +// try { +// imageGenerator.getBufferedImage(); +// Thread.sleep(4000); +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } +// imageGenerator.saveAsImage("z:/baidu.png"); +// } + + public static void main0(String[] args) { + getHtmlImage("aa"); + } + public static String getHtmlImage(String html){ +// String path = Global.getConfig("userfiles.basedir") + "/htmlImageTemp/"; + String path = ""; + FileUtils.createDirectory(path); + path += IdGen.uuid() + ".png"; + getHtmlImage(html,path); + return path; + } + public static void getHtmlImage(String html, String path){ + getHtmlImage(html,8000,path); + } + public static void getHtmlImage(String html, int sleep, String path){ +// HtmlImageGenerator imageGenerator = new HtmlImageGenerator(); +// imageGenerator.loadHtml(html); +// imageGenerator.setSize(new Dimension(595,842)); +// try { +// imageGenerator.getBufferedImage(); +// Thread.sleep(sleep); +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } +// imageGenerator.saveAsImage(path); + } + /** + * 表格向下扩展数据行,因为POI对word格式支持较差,故我们做一些约定
+ * 1.约定表格要有一行模板行,用于扩展数据行的样式效仿;
+ * 2.模板表格里最好不要有合并行,如非常需要,则可以用隐藏单元格或设置白色边框的方式巧妙替代合并行;
+ * 3.模板行下方不允许出现其他行
+ * @param tableIndex 表格角标,第1个表为0 + * @param tmpRowIndex 模板行角标,第1行为0(模板行不是标题行,而是扩展数据行样式的效仿对象) + * @param isDelTmpRow true-删除模板行;false-不删除模板行 + * @param rowlist 要插入的数据集合,格式为[行集合<列集合>,列集合的长度最好与模板行长度一致] + */ + public WordOperator insert2Table(int tableIndex, int tmpRowIndex, boolean isDelTmpRow, List> rowlist) { + // 因为需要对rowlist进行添加操作,故而我们重新做一个变量,防止因为改变而产生问题 + List> dataList = new ArrayList<>(rowlist); + // 根据角标取出表格对像 + XWPFTable table = doc.getTables().get(tableIndex); + // 再根据行号获取模板行 + XWPFTableRow tmpRow = table.getRow(tmpRowIndex); + // 不知道是什么原因,最后一列数据会跑到模板行上一行,故而这里在最末添加一组数据,解决这个bug + dataList.add(new ArrayList<>()); + // 遍历数据集合 + for (int i = 0, len = dataList.size(); i < dataList.size(); i++) { + // 计算数据行编号 + int dataIndex = tmpRowIndex + 1 + i; + // 按照模板行样式添加一行到数据行 + table.addRow(tmpRow, dataIndex); + if (i < len - 1) { + // 取出这一行 + XWPFTableRow row = table.getRow(dataIndex); + // 取出这一行全部单元格 + List cellList = row.getTableCells(); + // 取出这一行对应的数据,数据与单元格角标位置是一致的,一一替换文本 + List colList = dataList.get(i); + for (int j = 0; j < cellList.size(); j++) { + setCellText(cellList.get(j), colList, j); + } + } + } + // 删掉那一行为了bug而添加的空白行(因为空白行跑到模板行上一行,所以tmpRowIndex为该行角标) + table.removeRow(tmpRowIndex); + // 删除模版行(因为上面已经删除了空白行了,所以tmpRowIndex就变成了下一行模板行了) + if (isDelTmpRow) { + table.removeRow(tmpRowIndex); + } + return this; + } + + /** + * 替换单元格文本 + * @param cell 单元格 + * @param colList 替换文本的集合(角标越位了,就置空) + * @param j 单元格角标,也就是文本集合的角标 + */ + private void setCellText(XWPFTableCell cell, List colList, int j) { +// cell.setColor("FF0000"); + // 无论一个单元格被切割成多少个run,只需要修改第一个即可,改完就return掉了 + List paragraphs = cell.getParagraphs(); + for (XWPFParagraph p : paragraphs) { + List runs = p.getRuns(); + for (XWPFRun r : runs) { + try { + r.setText(colList.get(j), 0); + //染上背景色 +// r.getCTR().addNewRPr().addNewHighlight().setVal(STHighlightColor.YELLOW); + } catch (Exception e) { + r.setText("", 0); + } + return; + } + } + } + + /** + * 填充颜色 + * @param tableIndex 第几个表格,第一个表格为0 + * @param rowIndex 表格中的第几行,从0开始算起 + * @param colIndex 行中的第几单元格,从0开始算起 + * @param color 颜色,rgbStr - the desired cell color, in the hex form "RRGGBB". + * @return + */ + public WordOperator fillCollorTable(int tableIndex, int rowIndex, int colIndex, String color) { + // 根据角标取出表格对像 + XWPFTable table = doc.getTables().get(tableIndex); + if(table == null){ + logger.error("没有找到表格!tableIndex: [{}] row: [{}] col: [{}]",tableIndex,rowIndex,colIndex); + return this; + } + XWPFTableRow row = table.getRow(rowIndex); + if(row == null){ + logger.error("没有找到row!tableIndex: [{}] row: [{}] col: [{}]",tableIndex,rowIndex,colIndex); + return this; + } + XWPFTableCell col = row.getCell(colIndex); + if(col == null){ + logger.error("没有找到col!tableIndex: [{}] row: [{}] col: [{}]",tableIndex,rowIndex,colIndex); + return this; + } + col.setColor(color); + return this; + } + + /** + * 填充颜色 + * @param tableIndex 第几个表格,第一个表格为0 + * @param rowIndex 表格中的第几行,从0开始算起 + * @param color 颜色,rgbStr - the desired cell color, in the hex form "RRGGBB". + * @return + */ + public WordOperator fillRowCollorTable(int tableIndex, int rowIndex, String color) { + // 根据角标取出表格对像 + XWPFTable table = doc.getTables().get(tableIndex); + if(table == null){ + logger.error("没有找到表格!tableIndex: [{}] row: [{}]",tableIndex,rowIndex); + return this; + } + XWPFTableRow row = table.getRow(rowIndex); + if(row == null){ + logger.error("没有找到row!tableIndex: [{}] row: [{}]",tableIndex,rowIndex); + return this; + } + List cellList = row.getTableCells(); + cellList.forEach(col -> col.setColor(color)); + return this; + } + + /** + * 填充颜色 + * @param tableIndex 第几个表格,第一个表格为0 + * @param colIndex 表格中的第几列,从0开始算起 + * @param color 颜色,rgbStr - the desired cell color, in the hex form "RRGGBB". + * @return + */ + public WordOperator fillColCollorTable(int tableIndex, int colIndex, String color) { + // 根据角标取出表格对像 + XWPFTable table = doc.getTables().get(tableIndex); + if(table == null){ + logger.error("没有找到表格!tableIndex: [{}] row: [{}]",tableIndex,colIndex); + return this; + } + List rows = table.getRows(); + rows.forEach(row -> { + List cellList = row.getTableCells(); + for (int i = 0; i < cellList.size(); i++) { + if(i == colIndex){ + cellList.get(i).setColor(color); + } + } + }); + return this; + } + + /** + * 给定输出流即可将word文档导出到 + */ + public void write(OutputStream os) throws Exception { + doc.write(os); + } + + /** + * + * 功能说明 : 补充空列 + * 创建者 : byx + * 修改日期 : 2018年12月25日 + * @param returnList:填充到这个list中 + * @param inputList:输入的list,业务信息列表,用来计算空列的初始值 + * @param endRowNum:总共N行 + * @param colNum:拥有几列 + */ + public static void fillNullCall(List> returnList, List inputList, int endRowNum, int colNum){ + int fillNum = 0; + if(inputList != null && !inputList.isEmpty()){ + fillNum = inputList.size(); + } + WordOperator.fillNullCall(returnList,fillNum,endRowNum,colNum); + + } + + /** + * + * 功能说明 : 补充空列 + * 创建者 : byx + * 修改日期 : 2018年12月25日 + * @param returnList:填充到这个list中 + * @param startRowNum:从N开始循环 + * @param endRowNum:总共N行 + * @param colNum:拥有几列 + */ + public static void fillNullCall(List> returnList, int startRowNum, int endRowNum, int colNum){ + for(int i = startRowNum;i < endRowNum; i++){ + List twoList = Lists.newArrayList(); + for (int j = 0; j < colNum; j++) { + twoList.add(""); + } + returnList.add(twoList); + } + } + + /** + * 根据第几个表格删除第几行,单独删除 + * @param tableIndex 第几个表格 + * @param rowIndex 删除第几行 + * @return + */ + public WordOperator removeTableRow(int tableIndex, int rowIndex,int colIndex) { + // 根据角标取出表格对像 + XWPFTable table = doc.getTables().get(tableIndex); + if(table == null){ + logger.error("没有找到表格!tableIndex: [{}] row: [{}]",tableIndex,rowIndex); + return this; + } +// XWPFTableRow row = table.getRow(rowIndex); +// XWPFTableRow nextRow = table.getRow(rowIndex + 1); +// XWPFTableCell col = row.getCell(colIndex); +// XWPFTableCell nextCol = nextRow.getCell(colIndex); +// +// System.out.println("文字内容:" + col.getText()); +// List list = Lists.newArrayList(); +// list.add(StringUtils.isBlank(col.getText())?"空的??":col.getText()); +// setCellText(nextCol,list,0); +// XWPFTableCell nextCol = nextRow.getCell(0); +// if (nextCol.getCTTc().getTcPr() == null) nextCol.getCTTc().addNewTcPr(); +// if (nextCol.getCTTc().getTcPr().getGridSpan() == null) nextCol.getCTTc().getTcPr().addNewGridSpan(); +// nextCol.getCTTc().getTcPr().getGridSpan().setVal(rowSpan.subtract(new BigInteger("1"))); + table.removeRow(rowIndex); + return this; + } + + /** + * 根据第几个表格删除多行 + * @param tableIndex 第几个表格 + * @param rowIndex 删除第几行的集合 + * @return + */ + public WordOperator removeTableRow(int tableIndex, List rowIndex,int colIndex) { + rowIndex.forEach(x -> removeTableRow(tableIndex,x,colIndex)); + return this; + } + + /** + * + * 功能说明 : 文档再次替换时使用,因为之前已经替换一次了,所有需要将文档片段存入内存流中,在读取到文档类里,才能替换成功 + * 创建者 : byx + * 修改日期 : 2018年9月26日 + * @param wo 文档片段对象(已经{替换内容}替换过) + * @param result 替换的内容 + * @return 重新替换的文档片段 + */ + public static WordOperator twoReplaceWord(WordOperator wo,Map result){ + WordOperator w2o = wo; + try { + w2o = twoReplaceWord(wo); + w2o.replaceTextPlus(result); + } catch (Exception e) { + e.printStackTrace(); + } + //再次替换 + return w2o; + } + + /** + * + * 功能说明 : 文档再次替换时使用,因为之前已经替换一次了,所有需要将文档片段存入内存流中,在读取到文档类里,才能替换成功 + * 创建者 : byx + * 修改日期 : 2018年9月26日 + * @param wo 文档片段对象(已经{替换内容}替换过) + * @return 重新替换的文档片段 + */ + public static WordOperator twoReplaceWord(WordOperator wo) throws Exception { + //先创建内存输出流 + ByteArrayOutputStream output = new ByteArrayOutputStream(); + //将文档先输出到 + wo.write(output); + //根据内存输出流创建内存输入流 + InputStream input = new ByteArrayInputStream(output.toByteArray()); + //用这个输入流创建文档对象 + WordOperator w2o = new WordOperator(input); + IoUtil.close(output); + IoUtil.close(input); + return w2o; + } + +} diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordTextStyle.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordTextStyle.java new file mode 100644 index 00000000..d19fc042 --- /dev/null +++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordTextStyle.java @@ -0,0 +1,255 @@ +/********************************************************************** +* $Id: WordImageLabel.java WordImageLabel ,v0.1 2018年9月19日 下午8:50:48 byx Exp $ +* Copyright ©2018 sida . All rights reserved +***********************************************************************/ + +package org.jeecg.modules.tools.word; + + +import lombok.Data; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.xwpf.usermodel.UnderlinePatterns; +import org.apache.poi.xwpf.usermodel.VerticalAlign; +import org.apache.poi.xwpf.usermodel.XWPFRun; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTShd; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.STHighlightColor; + +/** +* 功能说明:从标签中读取键值,宽高和后缀内容 +* 创建者:byx +* 创建时间:2018年9月19日 +*
+* 修改时间:       修改者:            
+* 修改内容:
+* 
+*/ +@Data +public class WordTextStyle { + + /** + * 颜色 + */ + private String color; + + public void runColor(XWPFRun run){ + if(run != null && StringUtils.isNotBlank(color)){ + run.setColor(color); + } + } + + /** + * 加粗 + */ + private Boolean isBold; + + public void runBold(XWPFRun run){ + if(run != null && isBold != null){ + run.setBold(isBold); + } + } + + /** + * 背景色(文本突出显示颜色) + */ + private STHighlightColor.Enum bColor; + + public void runBColor(XWPFRun run){ + if(run != null && bColor != null){ + run.getCTR().addNewRPr().addNewHighlight().setVal(bColor); + } + } + + /** + * 底纹 + */ + private String shading; + + public void runShading(XWPFRun run){ + if(run != null && StringUtils.isNotBlank(shading)){ + CTShd shd = run.getCTR().addNewRPr().addNewShd(); + shd.setFill(shading); + } + } + + /** + * 字体 + */ + private String fontFamily; + + public void runFontFamily(XWPFRun run){ + if(run != null && StringUtils.isNotBlank(fontFamily)){ + run.setFontFamily(fontFamily); + } + } + + /** + * 字体大小 + */ + private Integer fontSize; + + public void runFontSize(XWPFRun run){ + if(run != null && fontSize != null){ + run.setFontSize(fontSize); + } + } + + /** + * 阴影 + */ + private Boolean shadow; + + public void runShadow(XWPFRun run){ + if(run != null && shadow != null){ + run.setShadow(shadow); + } + } + + /** + * 浮雕???? + */ + private Boolean embossed; + + public void runEmbossed(XWPFRun run){ + if(run != null && embossed != null){ + run.setEmbossed(embossed); + } + } + + /** + * 删除线 + */ + private Boolean strikeThrough; + + public void runStrikeThrough(XWPFRun run){ + if(run != null && strikeThrough != null){ + run.setStrikeThrough(strikeThrough); + } + } + + /** + * 双删除线 + */ + private Boolean doubleStrikethrough; + + public void runDoubleStrikethrough(XWPFRun run){ + if(run != null && doubleStrikethrough != null){ + run.setDoubleStrikethrough(doubleStrikethrough); + } + } + + /** + * 大写???? + */ + private Boolean capitalized; + + public void runCapitalizedg(XWPFRun run){ + if(run != null && capitalized != null){ + run.setCapitalized(capitalized); + } + } + + /** + * 印记???? + */ + private Boolean imprinted; + + public void runImprinted(XWPFRun run){ + if(run != null && imprinted != null){ + run.setImprinted(imprinted); + } + } + + /** + * 斜体 + */ + private Boolean italic; + + public void runItalic(XWPFRun run){ + if(run != null && italic != null){ + run.setItalic(italic); + } + } + /** + * 小型大写字母?? + */ + private Boolean smallCaps; + + public void runSmallCaps(XWPFRun run){ + if(run != null && smallCaps != null){ + run.setSmallCaps(smallCaps); + } + } + /** + * 字符间距 + */ + private Integer kerning; + + public void runKerning(XWPFRun run){ + if(run != null && kerning != null){ + run.setKerning(kerning); + } + } + + /** + * 文本位置 + */ + private Integer textPosition; + + public void runTextPosition(XWPFRun run){ + if(run != null && textPosition != null){ + run.setTextPosition(textPosition); + } + } + + /** + * 下标 + */ + private VerticalAlign subscript; + + public void runSubscript(XWPFRun run){ + if(run != null && subscript != null){ + run.setSubscript(subscript); + } + } + + /** + * 下划线 + */ + private UnderlinePatterns underline; + + public void runUnderline(XWPFRun run){ + if(run != null && underline != null){ + run.setUnderline(underline); + } + } + + /** + * + * @param run + */ + public void run(XWPFRun run){ + runColor(run); + runBold(run); + runBColor(run); + runShading(run); + runFontFamily(run); + runFontSize(run); + runShadow(run); + runEmbossed(run); + runStrikeThrough(run); + runDoubleStrikethrough(run); + runCapitalizedg(run); + runImprinted(run); + runItalic(run); + runSmallCaps(run); + runKerning(run); + runTextPosition(run); + runSubscript(run); + runUnderline(run); + + } + + + + +} diff --git a/jeecg-module-main/src/main/resources/officetemplates/exp1/tpkqk.docx b/jeecg-module-main/src/main/resources/officetemplates/exp1/tpkqk.docx new file mode 100644 index 00000000..f0bc92f7 --- /dev/null +++ b/jeecg-module-main/src/main/resources/officetemplates/exp1/tpkqk.docx @@ -0,0 +1,37 @@ + {xqxn}学期听评课制度落实情况 + + 听课制度落实清空 + 学院(部)党政领导、学院(部)教务委员会委员、专业负责人、基层教学组织负责人、辅导员、专任教师等人员的听评课情况,建议学院(部)党政领导关注思政课的课堂教学效果。具体要求详见《本科教育教学质量管理实施办法》(附件4)、《关于本科教学听课制度的规定》(附件5)。 + +{xqxn}学期听课情况: + +学院(部)教务委员会委员人数:{oneListSize}人 + + 姓名 + 职务 + 听课次数 + + z + z + Z + +行政负责人、教学副院(部)长及系(专业)负责人等人数:{twoListSize}人 + + 姓名 + 职务 + 听课次数 + + z + z + z + +其他党政领导及辅导员人数:{threeListSize}人 + + 姓名 + 职务 + 听课次数 + + z + z + z + diff --git a/pom.xml b/pom.xml index 872b0cb2..ecbf0da0 100644 --- a/pom.xml +++ b/pom.xml @@ -393,6 +393,7 @@ eot ttf svg + docx