大文件word生成的处理与解决策略
前言
对于简单word文档的生成导出,java已经有着很多技术来进行处理,在有着相对固定的格式样板下,采用word模板导出相对会是比较好的选择。但是当数据量且包含大量图片后,采用模板导出就显得无力了,模板的缺点是无法应对动态复杂的数据文档生成,这时候采用动态生成word是唯一的选择。
问题背景:需要生成一个包含大量图片表格的word文档,该文档内容在百兆与1G中间
可以看到该模板是一个相当复杂的文件,既需要对不同类型的图片设置不同的格式还需要动态生成每个类型表格的位置,并将图片插入的word文件当中去
代码处理
controller:
package com.wlh.zetc.restore.controller;import com.wlh.zetc.common.core.util.R;
import com.wlh.zetc.restore.manage.LedgerSequenceManage;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** 台账生成** @author wanghailin* @date 2024-05-23 14:19:56*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/restoreLedger" )
@Tag(description = "restoreLedger" , name = "台账生成" )
//@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class LedgerSequenceController {private final LedgerSequenceManage ledgerSequenceManage;/*** 通过乡镇id来生成台账* @param regionId* @return R*/@Operation(summary = "通过乡镇id来生成台账" , description = "通过乡镇id来生成台账" )@GetMapping("/{regionId}" )
// @PreAuthorize("@pms.hasPermission('zetc_ledger_generate')" )public R getById(@PathVariable("regionId" ) Long regionId) {//log.info("Request thread=>start");System.out.println("Request thread=>start");ledgerSequenceManage.generateAndUpload(regionId);//Log.info("Request thread=>end");System.out.println("Request thread=>end");return R.ok("台账生成中,请稍后到台账中心下载最新文档");}
}
Manage:
package com.wlh.zetc.restore.manage;import cn.hutool.core.date.DateUtil;
import com.wlh.zetc.common.data.tenant.TenantContextHolder;
import com.wlh.zetc.common.security.util.SecurityUtils;
import com.wlh.zetc.restore.bo.SubRegionBO;
import com.wlh.zetc.restore.entity.Activity;
import com.wlh.zetc.restore.entity.RestoreFileEntity;
import com.wlh.zetc.restore.entity.RestoreRegionEntity;
import com.wlh.zetc.restore.enums.LedgerTypeEnum;
import com.wlh.zetc.restore.service.*;
import com.wlh.zetc.restore.service.impl.QiniuServiceImpl;
import com.wlh.zetc.restore.utils.FormatProcessToWordUtils;
import com.wlh.zetc.restore.utils.StrategicChoicesUtils;
import com.wlh.zetc.restore.utils.TextUtils;
import lombok.AllArgsConstructor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;/*** 至农台账导出顺序梳理管理** @author wanghailin* @date 2024-05-8 15:49:33*/
@Service
@AllArgsConstructor
public class LedgerSequenceManage {private final RestoreFileService fileService;private final RestoreInStoreService inStoreService;private final RestoreMatterService matterService;private final RestoreOutStoreService outStoreService;private final RestorePatrolTaskService patrolTaskService;private final RestoreUsePlanService usePlanService;private final List<List<Activity>> activities;private final RestoreRegionService regionService;private final StrategicChoicesUtils strategicChoicesUtils;private final FormatProcessToWordUtils formatProcessToWordUtils;private final QiniuServiceImpl qiniuService;//通过乡镇id,来给所在乡镇下的村数据排序public List<List<Activity>> sequence(Long regionId,String streetTown){//清空数据收集池中的数据strategicChoicesUtils.reset();//1.根据乡镇id拿到该乡镇的名称//2.拿到该乡镇下所有的村id(分为2种情况)//2.1 村id集合String groupSubRegionId = regionService.getGroupSubRegionId(regionId);//2.2 村id单独List<SubRegionBO> subRegionList = regionService.getSubRegionId(regionId);//乡镇为单位//发货单(照片)List<Activity> materialDeliveryData = fileService.getMaterialDeliveryData(groupSubRegionId);//物资到货(照片)List<Activity> inStoreData = inStoreService.getInStoreData(groupSubRegionId);//控地巡视(照片)List<Activity> patrolLandData = patrolTaskService.getPatrolLandData(groupSubRegionId);//旱地种植结构调整照片List<Activity> apsdData = fileService.getAPSDData(groupSubRegionId);//产品照片List<Activity> productData = fileService.getProductData(groupSubRegionId);//会议照片List<Activity> meetData = fileService.getMeetData(groupSubRegionId);//喷施路径照片List<Activity> sprayPathData = usePlanService.getSprayPathData(groupSubRegionId);//数据汇集strategicChoicesUtils.collect(materialDeliveryData).collect(inStoreData).collect(patrolLandData).collect(apsdData).collect(productData).collect(meetData).collect(sprayPathData);//村为单位//出库、施工、回收subRegionList.forEach(subRegion -> {//用于村分隔处理List<Activity> villageSeparation = new ArrayList<>();Activity village = new Activity();village.setRegionName(subRegion.getRegionName());village.setSeparateFlag(true);villageSeparation.add(village);strategicChoicesUtils.collect(villageSeparation);List<String> matterNameList = matterService.getMatterName(Long.valueOf(subRegion.getRegionId()));if (!matterNameList.isEmpty()) {matterNameList.forEach(matterName -> {//出库(照片)List<Activity> outStoreData = outStoreService.getOutStoreData(subRegion.getRegionId(), matterName);//施工过程(照片)List<Activity> usePlanData = usePlanService.getUsePlanData(subRegion.getRegionId(), matterName);//包装袋回收(照片)(需签名)List<Activity> packBackData = usePlanService.getPackBackData(subRegion.getRegionId(), matterName);strategicChoicesUtils.collect(outStoreData).collect(usePlanData).collect(packBackData);});}//水分管理(照片)List<Activity> patrolWaterData = patrolTaskService.getPatrolWaterData(subRegion.getRegionId());strategicChoicesUtils.collect(patrolWaterData);});//自定义规制处理器return strategicChoicesUtils.handle(strategicChoicesUtils.getCollectedListActivities(),streetTown);}//异步调用该方法生成并上传文档@Asyncpublic void generateAndUpload(Long regionId){try {
// Log.info("Ledger generation thread => start");System.out.println("Ledger generation thread => start");RestoreRegionEntity region = regionService.getById(regionId);String streetTown = region.getRegionName();List<List<Activity>> sequence = sequence(regionId,streetTown);InputStream inputStream = formatProcessToWordUtils.exportActivitiesToWord(sequence);// 上传到服务器String filename = streetTown + DateUtil.today() +"-"+ System.currentTimeMillis()/1000+".docx";String project = "ledger"+ TenantContextHolder.getTenantId()+"/";String key = project+ TextUtils.generateFileName(filename);// 上传完后的伪地址String pseudoAddress = qiniuService.uploadImage2qiniu(inputStream, key);strategicChoicesUtils.reset();// 获取到的地址保存在数据库表中,以供后续下载(放在文件表中使用不同类型区分)RestoreFileEntity wordFile = new RestoreFileEntity();wordFile.setRegionId(regionId);wordFile.setFileUrl(pseudoAddress);wordFile.setFileType(LedgerTypeEnum.LEDGER.getType());wordFile.setFileUse(LedgerTypeEnum.LEDGER.getUse());wordFile.setFileSuffix(LedgerTypeEnum.LEDGER.getSuffix());
// wordFile.setCreateBy(SecurityUtils.getUser().getUsername());wordFile.setCreateBy("test");fileService.save(wordFile);
// Log.info("Ledger generation thread => end");System.out.println("Ledger generation thread => end");} catch (Exception e) {e.printStackTrace();}}}
Util:
package com.wlh.zetc.restore.utils;import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.wlh.zetc.restore.entity.Activity;
import com.wlh.zetc.restore.enums.GenerateTypeEnum;
import com.wlh.zetc.restore.service.impl.QiniuServiceImpl;
import lombok.AllArgsConstructor;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHeight;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTrPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;
import org.springframework.stereotype.Service;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigInteger;
import java.util.List;
/*** 至农台账生成工具类** @author wanghailin* @date 2024-05-18 10:49:33*/
@Service
@AllArgsConstructor
public class FormatProcessToWordUtils {private final QiniuServiceImpl qiniuService;public InputStream exportActivitiesToWord(List<List<Activity>> activities) throws Exception {InputStream inputStream = null;try (XWPFDocument document = new XWPFDocument()) {for (int activityListIndex = 0; activityListIndex < activities.size(); activityListIndex++) {for (int activityIndex = 0; activityIndex < activities.get(activityListIndex).size(); activityIndex++) {Activity activity = activities.get(activityListIndex).get(activityIndex);if(activity.getSeparateFlag() != null && activity.getSeparateFlag()){if(StringUtils.isNotEmpty(activity.getRegionName())){for (char ch : activity.getRegionName().toCharArray()) {XWPFParagraph paragraph = document.createParagraph();paragraph.setAlignment(ParagraphAlignment.CENTER); // 设置段落居中XWPFRun run = paragraph.createRun();run.setText(String.valueOf(ch)); // 设置文本为当前字符run.setBold(true); // 设置加粗run.setFontFamily("宋体"); // 设置字体为宋体run.setFontSize(72); // 设置字体大小为72号// 换行,每个字一行if (ch != activity.getRegionName().charAt(activity.getRegionName().length() - 1)) {run.addBreak();}}}// 在内容后添加分页符XWPFParagraph breakParagraph = document.createParagraph();XWPFRun breakRun = breakParagraph.createRun();breakRun.addBreak(BreakType.PAGE);}// 标题仅在第一个Activity中添加if (activityIndex == 0) {XWPFParagraph titleParagraph = document.createParagraph();titleParagraph.setAlignment(ParagraphAlignment.CENTER);XWPFRun titleRun = titleParagraph.createRun();titleRun.setText(activity.getTitle());titleRun.setBold(true);titleRun.setFontFamily("宋体");titleRun.setFontSize(22); // 二号字体大约是22pt}if (activity != null && activity.getType() != null){if(!activity.getType().equals(GenerateTypeEnum.PATROL.getCode())|| !activity.getType().equals(GenerateTypeEnum.DELIVERY.getCode())){// 次标题XWPFParagraph subTitleParagraph = document.createParagraph();subTitleParagraph.setAlignment(ParagraphAlignment.CENTER);XWPFRun subTitleRun = subTitleParagraph.createRun();subTitleRun.setText(getCircleNumber(activityIndex + 1)); // 使用圆圈数字编号subTitleRun.setBold(true);subTitleRun.setFontFamily("宋体");subTitleRun.setFontSize(22);}}//发货单 1*n 表格if (activity != null && activity.getType() != null) {if (activity.getType().equals(GenerateTypeEnum.DELIVERY.getCode())) {// 表格List<String> urls = activity.getUrls(); // 图片URL列表int rows = 0;if (urls != null && urls.size() > 0) {rows = urls.size(); // n行,每个URL一个单元格}XWPFTable table = document.createTable(rows, 1); // 创建n*1的表格// 设置表格宽度CTTblWidth width = table.getCTTbl().addNewTblPr().addNewTblW();width.setType(STTblWidth.DXA);width.setW(BigInteger.valueOf(8500)); // 将宽度设置为原来的两倍,大约30.06厘米try (CloseableHttpClient httpClient = HttpClients.createDefault()) {if (urls != null && urls.size() > 0) {// 填充表格数据并设置单元格宽度for (int i = 0; i < urls.size(); i++) {XWPFTableRow row = table.getRow(i); // 获取当前行XWPFTableCell cell = row.getCell(0); // 获取行中的唯一单元格cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);XWPFParagraph cellParagraph = cell.getParagraphs().get(0);cellParagraph.setAlignment(ParagraphAlignment.CENTER);XWPFRun run = cellParagraph.createRun();run.setBold(true); // 设置文本加粗// 设置单元格高度CTTrPr trpr = row.getCtRow().isSetTrPr() ? row.getCtRow().getTrPr() : row.getCtRow().addNewTrPr();CTHeight ht = trpr.sizeOfTrHeightArray() > 0 ? trpr.getTrHeightArray(0) : trpr.addNewTrHeight();if (StringUtils.isNotEmpty(activity.getTitle()) && i <= 2) {ht.setVal(BigInteger.valueOf(6350)); // 设置行高为11.2厘米对应的DXA单位} else {ht.setVal(BigInteger.valueOf(6550)); // 设置行高为12厘米对应的DXA单位}download(run, httpClient, urls.get(i), activity.getType()); // 假设download方法用于处理图片下载和显示}}} catch (IOException e) {e.printStackTrace();}} else {// 表格List<String> urls = activity.getUrls(); // 图片URL列表int rows = 0;if (urls != null && urls.size() > 0) {rows = (int) Math.ceil(urls.size() / 2.0);}XWPFTable table = document.createTable(rows, 2);// 设置表格宽度CTTblWidth width = table.getCTTbl().addNewTblPr().addNewTblW();width.setType(STTblWidth.DXA);width.setW(BigInteger.valueOf(8500)); // 大约15.03厘米try (CloseableHttpClient httpClient = HttpClients.createDefault()) {if (urls != null && urls.size() > 0) {// 填充表格数据并设置单元格高度for (int i = 0; i < urls.size(); i++) {int rowIndex = i / 2;int colIndex = i % 2;XWPFTableRow row = table.getRow(rowIndex);// 确保行有足够的单元格while (row.getTableCells().size() <= colIndex) {row.createCell();}XWPFTableCell cell = row.getCell(colIndex);cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);XWPFParagraph cellParagraph = cell.getParagraphs().get(0);cellParagraph.setAlignment(ParagraphAlignment.CENTER);XWPFRun run = cellParagraph.createRun();run.setBold(true); // 设置文本加粗// 设置单元格高度CTTrPr trpr = row.getCtRow().isSetTrPr() ? row.getCtRow().getTrPr() : row.getCtRow().addNewTrPr();CTHeight ht = trpr.sizeOfTrHeightArray() > 0 ? trpr.getTrHeightArray(0) : trpr.addNewTrHeight();if (StringUtils.isNotEmpty(activity.getTitle()) && i <= 4) {ht.setVal(BigInteger.valueOf(6350)); // 设置行高为11.2厘米对应的DXA单位} else {ht.setVal(BigInteger.valueOf(6550)); // 设置行高为12厘米对应的DXA单位}download(run, httpClient, urls.get(i), activity.getType());}}} catch (IOException e) {e.printStackTrace();}}}// 在每个Activity处理完毕后添加分页符XWPFParagraph breakParagraph = document.createParagraph();XWPFRun breakRun = breakParagraph.createRun();breakRun.addBreak(BreakType.PAGE);}}// 保存Word文件到InputStreamByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();try {document.write(byteArrayOutputStream);} finally {byteArrayOutputStream.close();}// 创建InputStreaminputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());}return inputStream;}/*** 根据序列号生成对应的带圈数字。* @param number 序列号(1到10)* @return 带圈数字的字符串表示,如果序列号超出范围,则返回null。*/public String getCircleNumber(int number) {if (number < 1 || number > 10) {return null; // 序列号超出范围}return String.valueOf((char) ('\u2460' + number - 1));}/*** 根据url下载图片*/public void download(XWPFRun run, CloseableHttpClient httpClient, String url,Integer type) throws UnsupportedEncodingException {// 下载并插入图片// 拼接出可以访问下载得七牛云图片地址String downloadUrl = qiniuService.getPrivateDownloadUrl(url);HttpGet httpGet = new HttpGet(downloadUrl);try (CloseableHttpResponse response = httpClient.execute(httpGet)) {if (response.getStatusLine().getStatusCode() == 200) {// 将图片内容缓存到内存中ByteArrayOutputStream baos = new ByteArrayOutputStream();response.getEntity().writeTo(baos);byte[] imageBytes = baos.toByteArray();// 从缓存的数据创建一个新的ByteArrayInputStream用于读取图片尺寸InputStream sizeStream = new ByteArrayInputStream(imageBytes);BufferedImage image = ImageIO.read(sizeStream);double originalWidth = image.getWidth();double originalHeight = image.getHeight();double aspectRatio = originalHeight / originalWidth;Integer width = 200;// 根据宽度和宽高比计算高度if (type.equals(GenerateTypeEnum.DELIVERY.getCode())){width = width * 2;}double widthEmus = Units.toEMU(width); // 设定的宽度,单位为EMUdouble heightEmus = widthEmus * aspectRatio; // 根据宽高比计算的高度,单位为EMUif(heightEmus > 5000000.0){heightEmus = heightEmus * 0.75;}// 从缓存的数据创建一个新的ByteArrayInputStream用于插入图片InputStream insertStream = new ByteArrayInputStream(imageBytes);// 插入图片run.addPicture(insertStream, XWPFDocument.PICTURE_TYPE_JPEG, url, (int) widthEmus, (int) heightEmus);}} catch (ClientProtocolException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (InvalidFormatException e) {e.printStackTrace();}}
}
Util:
package com.wlh.zetc.restore.utils;import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.wlh.zetc.restore.entity.Activity;
import com.wlh.zetc.restore.enums.PatrolPatrolTypeEnum;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;/*** 至农台账生成决策数据处理*/
@Service
public class StrategicChoicesUtils {private static final List<List<Activity>> activities = new ArrayList<>();// 收集需要生成的activitypublic StrategicChoicesUtils collect(List<Activity> activityList) {if (activityList != null && !activityList.isEmpty()) {activities.add(new ArrayList<>(activityList));}return this; // 返回当前对象以支持链式调用}public List<List<Activity>> handle(List<List<Activity>> activitiesList,String streetTown) {for (List<Activity> activities : activitiesList) {for (Activity activity : activities) {if (activity.getType() != null) {String title = getTitleBasedOnType(activity,streetTown);activity.setTitle(title);}}}return activitiesList; // 返回处理后的List<List<Activity>>}private String getTitleBasedOnType(Activity activity,String streetTown) {String title = "";String regionName = StringUtils.isNotEmpty(activity.getRegionName()) ? activity.getRegionName() : "";String matterName = StringUtils.isNotEmpty(activity.getMatterName()) ? activity.getMatterName() : "";String date = StringUtils.isNotEmpty(activity.getDate()) ? activity.getDate() + "-" : "";switch (activity.getType()) {case 1: // DELIVERY(1,"发货单"),title = streetTown + regionName + "物资发货单";break;case 2: // ARRIVAL(2,"到货"),title = streetTown + regionName + matterName + "到货";break;case 3: // PATROL(3,"巡视"),String patrolTypeDesc = getPatrolTypeDesc(activity.getPatrolType());title = streetTown + date + patrolTypeDesc + "巡视";break;case 4: // APSD(4,"旱地种植结构调整"),title = "旱地种植结构调整情况";break;case 5: // PRODUCT(5,"产品"),title = matterName + "产品";break;case 6: // MEET(6,"会议"),title = "会议照片";break;case 7: // ROUTE(7,"喷施路径"),title = streetTown + matterName + "喷施路径";break;case 8: // OUTBOUND(8,"出库"),title = streetTown + regionName + matterName + "出库";break;case 9: // SPRINKLE(9,"施工过程"),title = streetTown + regionName + matterName + "施工过程";break;case 10: // RECOVERY(10,"包装袋回收"),title = streetTown + regionName + "包装袋回收";break;case 11: // water(11,"水分管理"),title = streetTown + regionName + "水分管理";break;case 0: // OTHER(0,"其它")title = "其它";break;}return title;}private String getPatrolTypeDesc(Integer patrolType) {if (patrolType == null) return "";switch (patrolType) {case 1: return PatrolPatrolTypeEnum.WATER.getDesc();case 2: return PatrolPatrolTypeEnum.CONTROL.getDesc();case 3: return PatrolPatrolTypeEnum.FLIGHT.getDesc();case 4: return PatrolPatrolTypeEnum.SPRINKLING.getDesc();case 5: return PatrolPatrolTypeEnum.OTHER.getDesc();default: return "";}}// 获取累积后的Activity列表public List<Activity> getCollectedActivities() {return activities.stream().flatMap(List::stream) // 将List<List<Activity>>转换为Stream<Activity>.collect(Collectors.toList()); // 将Stream<Activity>收集到List中}// 获取累积后的List<Activity>public List<List<Activity>> getCollectedListActivities() {return activities;}// 重置activities列表,以便重新开始收集public StrategicChoicesUtils reset() {activities.clear();return this;}
}
七牛云文件上传
maven:
<!-- 七牛云sdk --><dependency><groupId>com.qiniu</groupId><artifactId>qiniu-java-sdk</artifactId><version>7.7.0</version></dependency><!-- 图片信息获取 --><dependency><groupId>com.drewnoakes</groupId><artifactId>metadata-extractor</artifactId><version>2.18.0</version></dependency>
yml:
oss:qiniu:domain: qiniu.znkj0215.com # 访问域名(正式访问域名地址) 暂未配置https
# domain: qiniu.iswhl.com # 访问域名(测试访问域名地址) 已配置httpsaccessKey: APlM_0fW1A_PRS5bQ92rdGf9oSW-5q9mZK3Tv6yk # 公钥secretKey: Ri2eN9h4htBjZa8J8n_7QBfsAAvM_Arz5_CLqWth # 私钥bucketName: zhinonggengdi #存储空间名称
service:
package com.wlh.zetc.restore.service;import java.io.InputStream;
import java.io.UnsupportedEncodingException;/*** 七牛文件存储** @author wanghailin* @date 2024-03-12 10:21:35*/
public interface QiniuService {String uploadImage2qiniu(InputStream in, String key);boolean deleteImageFromQiniu(String key);String getPrivateDownloadUrl(String fileName) throws UnsupportedEncodingException;}
impl:
package com.wlh.zetc.restore.service.impl;import com.google.gson.Gson;
import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.*;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import com.wlh.zetc.restore.properties.QiniuProperties;
import com.wlh.zetc.restore.service.QiniuService;
import com.wlh.zetc.restore.utils.TextUtils;
import io.netty.channel.unix.Unix;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;/*** 七牛文件存储** @author wanghailin* @date 2024-03-12 10:21:35*/
@Service
public class QiniuServiceImpl implements QiniuService
{private final String domain;private final String bucketName;private final String ak;private final String sk;// 七牛文件上传管理器private final Configuration cfg;private final Auth auth;@Autowiredpublic QiniuServiceImpl(QiniuProperties oss){this.ak = oss.getAccessKey();this.sk = oss.getSecretKey();this.domain = oss.getDomain(); // CDN域名this.bucketName = oss.getBucketName();// //构造一个带指定 Region 对象的配置类cfg = new Configuration(Zone.zone0());auth = Auth.create(ak,sk);}/*** 上传图片到七牛云* @return 图片url* */@Overridepublic String uploadImage2qiniu(InputStream in, String key){try {UploadManager uploadManager = new UploadManager(cfg);// 根据命名空间生成的上传tokenString upToken = auth.uploadToken(bucketName);Response response = uploadManager.put(in,key,upToken,null, null);//解析上传成功的结果DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);//System.out.println(putRet.key);//System.out.println(putRet.hash);//return String.format("http://%s/%s",this.domain,putRet.key);return putRet.key;} catch (QiniuException ex) {Response r = ex.response;System.err.println(r.toString());try {System.err.println(r.bodyString());} catch (QiniuException ex2) {//ignore}}return null;}/*** 删除图片* */@Overridepublic boolean deleteImageFromQiniu(String imageUrl){BucketManager bucketManager = new BucketManager(auth, cfg);try {String key= TextUtils.getKey(imageUrl);Response response = bucketManager.delete(bucketName,key);return response.isOK();} catch (QiniuException ex) {//如果遇到异常,说明删除失败System.err.println(ex.code());System.err.println(ex.response.toString());}return false;}/*** 获取文件下载路径** @param fileName* @return* @throws UnsupportedEncodingException*/public String getPrivateDownloadUrl(String fileName) throws UnsupportedEncodingException {//文件https访问配置DownloadUrl url = new DownloadUrl(domain, true, fileName);//DownloadUrl url = new DownloadUrl(domain, false, fileName);long expireInSeconds = 3600;//1小时,可以自定义链接过期时间long deadline = System.currentTimeMillis()/1000 + expireInSeconds;Auth auth = Auth.create(ak, sk);String urlString = null;try {urlString = url.buildURL(auth, deadline);} catch (QiniuException e) {throw new RuntimeException(e);}return urlString;}
}
导出效果
空白部分是因为数据缺失
相关文章:
大文件word生成的处理与解决策略
前言 对于简单word文档的生成导出,java已经有着很多技术来进行处理,在有着相对固定的格式样板下,采用word模板导出相对会是比较好的选择。但是当数据量且包含大量图片后,采用模板导出就显得无力了,模板的缺点是无法应…...
unity3d:GameFramework+xLua+Protobuf+lua-protobuf,与服务器交互收发协议
概述 1.cs收发协议,通过protobuf序列化 2.lua收发协议,通过lua-protobuf序列化 一条协议字节流组成 C#协议基类 CSPacketBase,SCPacketBaseC#用协议基类 proto生成的CS类,基于这两个基类。分别为CSPacketBase是客户端发送至服…...
二刷算法训练营Day30 | 回溯算法(6/6)
目录 详细布置: 1. 回溯总结 2. 332. 重新安排行程 3. 51. N 皇后 4. 37. 解数独 详细布置: 1. 回溯总结 回溯是递归的副产品,只要有递归就会有回溯,所以回溯法也经常和二叉树遍历,深度优先搜索混在一起&#x…...
【车载AI音视频电脑】200万像素迷你一体机
产品主要特点: -设备安装方便简洁,可通过3M胶直接将设备粘 贴到车前挡风玻璃上 -支持IE预览,手机,PAD实时预览, 支持电脑客 户端实时预览功能 -内置2路模拟高清, 每路均可达到200万像素。另 外可扩充2路1080P模拟…...
齐普夫定律在循环神经网络中的语言模型的应用
目录 齐普夫定律解释公式解释图与公式的关系代码与图的分析结论 使用对数表达方式的原因1. 线性化非线性关系2. 方便数据可视化和分析3. 降低数值范围4. 方便参数估计公式详细解释结论 来自:https://zh-v2.d2l.ai/chapter_recurrent-neural-networks/language-model…...
如何在Android Studio上发布Flutter应用
发布Flutter应用到Android平台是一个多步骤的过程,涉及配置应用、生成签名密钥、配置Gradle文件、构建发布版本APK等步骤。本文将详细介绍这些步骤,帮助你顺利发布Flutter应用。 1. 准备你的应用 在发布之前,确保你的应用在开发环境中运行良…...
C++ 字符串处理4-根据指定的分隔符将字符串分割为多个子串根据指定的分隔符将多个子串连接成一个字符串
1. 关键词 C 字符串处理 分割字符串 连接字符串 跨平台 2. strutil.h #pragma once#include <string> #include <vector>namespace cutl {/*** brief The type of vector strings used in this library.**/using strvec std::vector<std::string>;/*** b…...
微信小程序请求request封装
公共基础路径封装 // config.js module.exports {// 测试BASE_URL: https://cloud.chejj.cn,// 正式// BASE_URL: https://cloud.mycjj.com };请求封装 // request.js import config from ../config/baseUrl// 请求未返回时的loading const showLoading () > wx.showLoadi…...
Web前端不挂科:深入探索与实战指南
Web前端不挂科:深入探索与实战指南 在数字化时代的浪潮中,Web前端开发已成为一项炙手可热的技能。然而,对于许多初学者来说,如何避免在Web前端课程中挂科却成为了一道难题。本文将从四个方面、五个方面、六个方面和七个方面&…...
Golang | Leetcode Golang题解之第149题直线上最多的点数
题目: 题解: func maxPoints(points [][]int) (ans int) {n : len(points)if n < 2 {return n}for i, p : range points {if ans > n-i || ans > n/2 {break}cnt : map[int]int{}for _, q : range points[i1:] {x, y : p[0]-q[0], p[1]-q[1]if…...
京准电钟 NTP时间同步服务器助力水库水坝水利自动化建设
京准电钟 NTP时间同步服务器助力水库水坝水利自动化建设 京准电钟 NTP时间同步服务器助力水库水坝水利自动化建设 水库大坝监测系统主要包括渗流监测系统、流量监测系统、雨量监测系统、沉降监测系统组成。每一个监测系统由监测仪器及自动化数据采集装置(内置通信装…...
程序员应该具备什么职业素养?
程序员应该有什么职业素养? 作为一个程序员,拥有以下职业素养是非常重要的: 扎实的技术功底:作为程序员,首先要具备扎实的技术基础,包括编程语言、算法、数据结构等方面的知识,能够熟练地解决问…...
linux 安装sftp及使用sftp上传和下载
一、centos7 安装sftp 1.安装 OpenSSH 服务: sudo yum install openssh-server2.启动 SSH 服务,并设置为开机启动: sudo systemctl start sshd sudo systemctl enable sshd3.创建一个新用户,用于SFTP连接(替换your_…...
AI虚拟试穿技术:开启高保真、多场景、多样化服装组合的试穿应用
随着电子商务的快速发展,消费者对于在线购物体验的要求越来越高。特别是在服装领域,消费者渴望能够在购买前直观地了解服装的试穿效果。传统的虚拟试穿技术虽然已有一定的发展,但在不同场景下的高保真度和鲁棒性方面仍面临挑战。为此,我们研发了一种全新的AI虚拟试穿技术,…...
数栈xAI:轻量化、专业化、模块化,四大功能革新 SQL 开发体验
在这个数据如潮的时代,SQL 已远远超越了简单的查询语言范畴,它已成为数据分析和决策制定的基石,成为撬动企业智慧决策的关键杠杆。SQL 的编写和执行效率直接关系到数据处理的速度和分析结果的深度,对企业洞察市场动态、优化业务流…...
oppo手机精简包名列表
oppo广告机,coloros为13.0,测试机为oppo a1x 5g。 手机第一次开机后就全屏广告,被恶心了好几个月。现使用universal Android debolater进行卸载测试,其中: 不可卸载的: 开机广告:com.coloros.…...
Cisco Packet Tracer实验(二)
二、用交换机构建 LAN 构建物件如下: 四个PC 两个交换机 一个Multi Switch多功能拓展控制器 连线必须是这个直线!!!不是虚线 最后实现效果如下: 全部的线是绿的,就表示是通的。 尝试一下,看PC…...
Julia 数学函数
Julia 数学函数 Julia 是一种高性能的动态编程语言,特别适合于数值计算和科学计算。在数学领域,Julia 提供了丰富的内置函数,这些函数涵盖了从基本运算到高级数学运算的各个方面。本文将详细介绍 Julia 中的数学函数,并提供一些示例,帮助读者更好地理解和使用这些函数。 …...
[next.js] svgr/webpack
nextjs如何配置svg文件,使其像react组件一样导入? 当前next.js 开发环境我使用了--turbo 来开启turbopack加速文件构建,所以之前的一些webpack loader之类的无法正常工作。通过搜索发现一般都是使用svgr/webpack来处理svg,打开svgr官网发现…...
vue页面和 iframe多页面无刷新方案和并行存在解决方案
面临问题 : back的后台以jsp嵌套iframe为主, 所以在前端框架要把iframe无刷新嵌套和vue页面进行并行使用,vue的keep-alive只能对虚拟dom树 vtree 进行缓存无法缓存iframe,所以要对iframe进行处理 tab标签的切换效果具体参考若依框架的tab切换,可以去若依看源码,若依源码没有实…...
Leetcode498. 对角线遍历
Every day a Leetcode 题目来源:498. 对角线遍历 解法1:模拟 根据题目要求,矩阵按照对角线进行遍历。设矩阵的行数为 m,矩阵的列数为 n,我们仔细观察对角线遍历的规律可以得到如下信息: 一共有 mn−1 条…...
flume配置----a1.sources.r1.positionFile=xxxx.json
positionFile 的作用和用途 记录读取位置: positionFile 记录了 Flume 读取文件的当前位置(偏移量),确保在 Flume 重启或崩溃后,能够从上次读取的位置继续读取文件,而不是重新开始读取。这在处理大文件或长…...
Controller 自动化日志输出
Starter库 1.定义注解 Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) Documented public interface TraceLog {/*** 日志类型** return*/String type() default ""; }2.定义捕获日志接口方法 public interface ITraceLogProcess {void afterThr…...
css3中有哪些新属性(特性)?
在 CSS3 中引入了许多新的属性和特性,以下是其中一些主要的: Flexbox(弹性盒子布局):通过 display: flex 及其相关属性,实现灵活的布局方式,使得元素在容器中可以自动调整大小和位置。 Grid&am…...
SAP ABAP 之面向对象OO
文章目录 前言一、类的理解二、如何创建ABAP类 a.类的定义与构成 b.类的访问区域 c.特殊方法 d.类的继承 三、类中参数的使用 a.IMPORTING / EXPORTING b.CHANGING c.RETURNING d.EX…...
在VSCode中使用Vim
在VSCode中使用Vim,主要涉及到Vim插件的安装和配置。以下是在VSCode中使用Vim的详细步骤: 1. 安装Vim插件 打开VSCode:首先,启动你的VSCode编辑器。进入扩展面板:在VSCode的左侧活动栏中,点击扩展图标&am…...
鸿蒙低代码开发的局限性
在版本是DevEco Studio 3.1.1 Release,SDK是3.1.0(API9) 的基础上。 1、低代码插件没有WebView组件。 2、低代码插件没有空白的自定义组件,当前提供的所谓自定义组件,只能用列表中提供的组件来拼接新的组件。 3、使用ets代码自定义的组件&…...
Codeforces Round 952 (Div. 4) c++题解(A-H1)
开头 : 这场没打,今天vp了一下,写了A-G,然后就去吃饭了! 比赛链接 : Dashboard - Codeforces Round 952 (Div. 4) - Codeforces A 直接交换,输出即可 inline void solve(){string a , b ; cin >> a>> b ;char c a[0] ;a…...
人工智能将成为数学家的“副驾驶”
人工智能将成为数学家的“副驾驶” 数学传统上是一门独立的科学。1986年,安德鲁怀尔斯为了证明费马定理,退到书房里呆了7年。由此产生的证明往往很难让同事们理解,有些至今仍有争议。但近年来,越来越多的数学领域被严格地分解为各…...
自适应巡航控制技术规范(简化版)
自适应巡航控制技术规范(简化版) 1 系统概述2 功能需求3 性能需求4 功能激活条件5 功能抑制条件6 系统局限性1 系统概述 ACC 自适应巡航系统可自动控制纵向跟车距离,减轻驾驶员的工作量,即驾驶员无需频繁的踩制动和油门便可完成部分的驾驶任务,但责任主体仍然是驾驶员,驾…...
高端零食品牌有哪些/一键优化免费下载
摘要:了解.jmx文件格式类型,对jmeter二次开发与拓展有很大的帮助,当然也可以利用python对其进行一些处理(生成一些测试用例,对jmx文件进行 ”增删改查“)。一个完整用例的.jmx文件基本结构是这样,类似于xml结构(树状结…...
四川省住房与城乡建设厅网站/网址缩短
2月28日消息,瓜子二手车、毛豆新车网母公司车好多集团正式宣布,已完成15亿美元新一轮融资,投资方为软银愿景基金(SoftBank Vision Fund)。 本轮融资将重点用于加大产品技术研发投入,掌控产业变革核心生产力…...
php框架做网站好处/环球贸易网
L1-003 个位数统计 (15 分) 给定一个 k 位整数 Ndk−110k−1⋯d1101d0 (0≤di≤9, i0,⋯,k−1, dk−1>0),请编写程序统计每种不同的个位数字出现的次数。例如:给定 N100311,则有 2 个 0,3 个 1,和 1 个…...
晋城两学一做网站/四川省人民政府官网
让大家对IPv6 有初步了解IPv6诞生的背景和解决的问题IPv6的地址和地址类型IPv6的过渡技术和部署ipv4是2的32次方也就4G多个IP地址,大部份地址都在美国占了70%其它国家只有30%,中国只份了5A9B190C ,中国20几人才一个IP地址而美国1人9个&#…...
如何用织梦做网站详细教程/谷歌推广技巧
我们给出了一个(轴对齐的)矩形列表 rectangles 。 对于 rectangle[i] [x1, y1, x2, y2],其中(x1,y1)是矩形 i 左下角的坐标,(x2,y2)是该矩形右上角的坐标。 …...
可信网站图片logo安装/国外产品推广平台
本文转载:http://www.cnblogs.com/gis-crazy/archive/2013/03/17/2964132.html 查看公司项目代码时,存在这样一个问题:winform界面上有很多信息填写,提交后台服务器更新,但数据的合法验证及值的转换却不太敢恭维&#…...