一. 功能目标
现有一张背景图片,一张水印图片,希望将水印图片叠加到背景图片上方的指定位置。
本代码基于Spring boot框架搭建而成,外部发起请求,可以下载到合成后的图片。
注意:
- 合成图片的难点不在代码,而在寻找素材。底图和水印图片的尺寸一定要固定。
- 只有不断地调试水印图片在底图上的坐标,才能使得合成后的图片更加自然。
- 如有必要,可以适当的缩放图片。
- 背景图片:
- 水印图片:
- 希望实现的效果:
二. 代码实现
2.1 依赖
maven:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.9.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
<scope>provided</scope>
</dependency>
</dependencies>
2.2 图片叠加服务
- 合成图片 入口方法
public File generate() throws IOException{
// 底图
File sourceFile = new File(this.getClass().getResource("/background.png").getFile());
// 水印图片
File waterFile = new File(this.getClass().getResource("/studentHead.png").getFile());
// 最终合成
String finalFilePath = waterFile.getParentFile().getAbsolutePath() + File.separator + "idPhoto.png";
// 本次合成之前,删除上一次请求合成后留下的图片
deleteFile(new File(finalFilePath));
BufferedImage buffImg = watermark(sourceFile, waterFile, 1.0f);
generateWaterFile(buffImg, finalFilePath);
return new File(finalFilePath);
}
- 合成并构造新图片
/**
*
* 构造图片
* 生成水印并返回java.awt.image.BufferedImage
* @param file 源文件(图片)
* @param waterFile 水印文件(图片)
* @param alpha 透明度, 选择值从0.0~1.0: 完全透明~完全不透明
* @return BufferedImage
* @throws IOException
*/
private BufferedImage watermark(File file, File waterFile, float alpha) throws IOException {
// 获取底图
BufferedImage buffImg = ImageIO.read(file);
//获取底图的宽度
int buffImgWidth = buffImg.getWidth();
//获取底图的高度
int buffImgHight = buffImg.getHeight();
// 获取层图1
BufferedImage waterImg1 = ImageIO.read(waterFile);
// 创建Graphics2D对象,用在底图对象上绘图
Graphics2D g2d = buffImg.createGraphics();
// 获取层图的宽度
int waterImgWidth1 = waterImg1.getWidth();
// 获取层图的高度
int waterImgHeight1 = waterImg1.getHeight();
// 在图形和图像中实现混合和透明效果
// 当底图和水印图片都是png时,不要开启混合透明效果,否则可能导致水印图片被底图部分遮盖
//g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
// 绘制
//g2d.drawImage(waterImg1, 775, 450, waterImgWidth1, waterImgHeight1, null);
g2d.drawImage(waterImg1, 755, 340, 350, 430, null);
g2d.dispose();// 释放图形上下文使用的系统资源
return buffImg;
}
- 输出水印图片
/**
* 输出水印图片
*
* @param buffImg 图像加水印之后的BufferedImage对象
* @param savePath 图像加水印之后的保存路径
*/
private void generateWaterFile(BufferedImage buffImg, String savePath) {
int temp = savePath.lastIndexOf(".") + 1;
try {
ImageIO.write(buffImg, savePath.substring(temp), new File(savePath));
} catch (IOException e1) {
e1.printStackTrace();
}
}
- 删除文件
private void deleteFile(File file) {
if(!file.exists()) {
return;
}
if(file.isFile() || file.list()==null) {
file.delete();
}
}
- Controller
@Slf4j
@RestController
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class MgmController {
final private PictureService pictureService;
@GetMapping("/file")
public DeferredResult<ResponseEntity<FileSystemResource>> generatorFile() {
DeferredResult<ResponseEntity<FileSystemResource>> deferredResult = new DeferredResult<>(20 * 1000L);
try {
File file = pictureService.generate();
if (file != null) {
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Content-Disposition", "attachment; filename=" + file.getName());
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
headers.add("Last-Modified", new Date().toString());
headers.add("ETag", String.valueOf(System.currentTimeMillis()));
ResponseEntity<FileSystemResource> body = ResponseEntity.ok().headers(headers).contentLength(file.length())
.contentType(MediaType.parseMediaType("application/octet-stream")).body(new FileSystemResource(file));
deferredResult.setResult(body);
}
} catch (IOException e) {
e.printStackTrace();
}
deferredResult.onTimeout(() -> {
log.error("图片生成超时");
deferredResult.setErrorResult("图片生成超时");
});
deferredResult.onError((throwable) -> {
log.error("图片生成出错: " + throwable.getMessage());
deferredResult.setErrorResult("图片生成出错");
});
return deferredResult;
}