如何在AEM工作流中配置自定义DAM处理器生成Doc/Xls缩略图
解决AEM DAM中Doc/Xls文件缩略图生成无效的问题
首先,咱们先排查你可能遗漏的配置项,再给出自定义处理器的代码示例:
一、遗漏的关键配置检查
1. 工作流步骤的MIME类型过滤未更新
你提到添加了MIME类型,但要确认UPDATE ASSET工作流里的「Create Thumbnail」步骤是否真的包含这两个类型:
- 打开AEM的工作流模型编辑器,找到「Create Thumbnail」步骤的配置
- 在「MIME Type Filter」字段里,添加
application/msword和application/vnd.ms-excel(用逗号分隔) - 保存工作流模型并重新发布
2. MIME类型与扩展名的映射配置
确保AEM能正确识别.doc/.xls文件对应的MIME类型:
- 进入AEM Felix Console(/system/console/configMgr)
- 找到「Apache Sling MIME Types Support」配置
- 检查是否存在以下映射:
doc=application/mswordxls=application/vnd.ms-excel
- 如果没有,添加后保存配置
3. 自定义处理器的OSGi配置未正确绑定MIME类型
你的自定义处理器需要明确声明它处理的MIME类型,否则AEM不会调用它:
- 在处理器的OSGi组件注解中,添加
process.mime.type属性,指定要处理的两个MIME类型 - 同时设置合理的
process.rank值(比如100),确保优先级高于默认处理器,避免被覆盖
4. 缺失Office文件处理依赖库
AEM默认不包含Apache POI库(处理Office文件的核心依赖),你需要:
- 在项目pom.xml中添加POI相关依赖(poi、poi-scratchpad、poi-ooxml)
- 确保这些依赖被打包到你的OSGi bundle中,或者部署到AEM的容器里
5. 工作流运行用户权限不足
检查「dam-update-asset-service」用户是否有足够权限修改Asset的rendition节点:
- 确保该用户对DAM路径有
jcr:write和rep:write权限 - 必要时在权限编辑器中更新权限设置
二、自定义处理器代码示例
下面是一个完整的Office文件缩略图处理器实现,基于Apache POI读取文件内容并生成文本式缩略图(如果需要更复杂的页面渲染,可结合PDFBox先转成PDF再生成缩略图):
package com.yourcompany.dam.processors; import com.day.cq.dam.api.Asset; import com.day.cq.dam.api.DamProcessor; import com.day.cq.dam.api.processing.ProcessingContext; import com.day.cq.dam.api.processing.ProcessingException; import com.day.cq.dam.api.processing.ProcessingResult; import org.apache.commons.io.IOUtils; import org.apache.poi.hwpf.HWPFDocument; import org.apache.poi.hwpf.extractor.WordExtractor; import org.apache.poi.ss.usermodel.*; import org.osgi.service.component.annotations.Component; import javax.jcr.Session; import java.awt.*; import java.awt.image.BufferedImage; import java.io.*; import javax.imageio.ImageIO; @Component( service = DamProcessor.class, property = { "process.label=Office Document Thumbnail Processor", "process.mime.type=application/msword", "process.mime.type=application/vnd.ms-excel", "process.rank:Integer=100" } ) public class OfficeThumbnailProcessor implements DamProcessor { private static final int THUMBNAIL_WIDTH = 480; private static final int THUMBNAIL_HEIGHT = 640; private static final String THUMBNAIL_RENDITION_NAME = "cq5dam.thumbnail.480.480.png"; @Override public void process(Asset asset, ProcessingContext context, ProcessingResult result) throws ProcessingException { Session session = context.getSession(); InputStream assetStream = null; BufferedImage thumbnailImage = null; try { assetStream = asset.getOriginal().getStream(); String mimeType = asset.getMimeType(); // 根据MIME类型处理不同Office文件 if ("application/msword".equals(mimeType)) { thumbnailImage = generateDocThumbnail(assetStream); } else if ("application/vnd.ms-excel".equals(mimeType)) { thumbnailImage = generateXlsThumbnail(assetStream); } if (thumbnailImage != null) { // 将图片写入Asset的缩略图rendition ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(thumbnailImage, "png", baos); InputStream thumbStream = new ByteArrayInputStream(baos.toByteArray()); // 删除旧缩略图(如果存在) if (asset.getRendition(THUMBNAIL_RENDITION_NAME) != null) { asset.getRendition(THUMBNAIL_RENDITION_NAME).delete(); } // 添加新缩略图 asset.addRendition(THUMBNAIL_RENDITION_NAME, thumbStream, "image/png"); result.addMessage(ProcessingResult.Status.SUCCESS, "Successfully generated thumbnail for Office asset"); } else { result.addMessage(ProcessingResult.Status.FAILED, "Failed to generate thumbnail: No content extracted"); } } catch (Exception e) { throw new ProcessingException("Error processing Office asset thumbnail", e); } finally { IOUtils.closeQuietly(assetStream); } } private BufferedImage generateDocThumbnail(InputStream inputStream) throws IOException { HWPFDocument doc = new HWPFDocument(inputStream); WordExtractor extractor = new WordExtractor(doc); String content = extractor.getText(); doc.close(); return createTextBasedThumbnail(content); } private BufferedImage generateXlsThumbnail(InputStream inputStream) throws IOException { Workbook workbook = WorkbookFactory.create(inputStream); Sheet sheet = workbook.getSheetAt(0); // 取第一个工作表 StringBuilder contentBuilder = new StringBuilder(); for (Row row : sheet) { for (Cell cell : row) { contentBuilder.append(getCellValue(cell)).append("\t"); } contentBuilder.append("\n"); } workbook.close(); return createTextBasedThumbnail(contentBuilder.toString()); } private String getCellValue(Cell cell) { if (cell == null) return ""; switch (cell.getCellType()) { case STRING: return cell.getStringCellValue(); case NUMERIC: if (DateUtil.isCellDateFormatted(cell)) { return cell.getDateCellValue().toString(); } else { return String.valueOf(cell.getNumericCellValue()); } case BOOLEAN: return String.valueOf(cell.getBooleanCellValue()); case FORMULA: return cell.getCellFormula(); default: return ""; } } private BufferedImage createTextBasedThumbnail(String content) { // 创建空白图片 BufferedImage image = new BufferedImage(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = image.createGraphics(); // 设置背景和字体 g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT); g2d.setColor(Color.DARK_GRAY); g2d.setFont(new Font("Arial", Font.PLAIN, 12)); // 绘制文本,自动换行 String[] lines = content.split("\n"); int yPosition = 20; for (String line : lines) { if (yPosition > THUMBNAIL_HEIGHT - 20) break; g2d.drawString(line.trim(), 10, yPosition); yPosition += 15; } g2d.dispose(); return image; } }
依赖配置(pom.xml)
在你的项目pom.xml中添加以下POI依赖:
<dependencies> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.2</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> <version>4.1.2</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> <scope>compile</scope> </dependency> </dependencies>
最后验证步骤
- 部署自定义处理器bundle到AEM
- 检查Felix Console中该组件是否激活
- 重新上传一个.doc或.xls文件,查看UPDATE ASSET工作流是否完成,Asset的缩略图是否生成
内容的提问来源于stack exchange,提问作者Tarun Prakash
相关产品推荐
相关产品推荐

