【OSS】图片加水印与上传

​强烈推荐一个大神的人工智能的教程:http://www.captainai.net/zhanghan

一、效果图

1.未添加水印图

在这里插入图片描述

2.添加水印效果图

在这里插入图片描述

二、需求场景

在上传的图片上需要加上时间水印

三、加水印

在上代码前有几个问题:

1. 图片上设置水印,字体如何设置?

答:使用java.awt.FONT,这个类支持多少种字体?可以通过下列方法查询都有哪些字体,然后设置自己业务需要的字体

/**
     * 支持多少种字体
     *
     * @param args
     */
    public static void main(String[] args) {
        GraphicsEnvironment grapEnv = GraphicsEnvironment.getLocalGraphicsEnvironment();
        String[] fontNameList = grapEnv.getAvailableFontFamilyNames();

        for (String fontName : fontNameList) {

            System.out.println(fontName);

        }
    }

2.图片有大有小,如何使水印大小自适应

答:分别计算图片宽、高和水印宽、高,若水印宽或高超出图片的宽或高,则按照一边的比例做等比缩放,参考代码中的方法:getWaterMarkMetrics

3.水印位置如何设置

答:水印位置有左上角,右上角,左下角,右下角,中间

4.图片本身颜色各异,水印如何支持在所有图片上都可以清晰展示?

答:例如图片底色为黑色,水印为黑色,则会导致水印展示不出来;解决方案是确定一个合理的水印颜色,给水印添加一个背景,例如水印为黑色,那就给水印加一个白色的背景,白色整个覆盖到图片上会覆盖原图的内容,通过设置透明度实现添加的水印不覆盖原图内容

5.废话不多说,直接上代码

    // 300像素
    private final static Integer PIXEL_300 = 300;
    // 20像素
    private final static Integer PIXEL_20 = 20;

    private final static String WATER_MARK_WIDTH = "waterMarkWidth";
    private final static String WATER_MARK_HEIGHT = "waterMarkHeight";
    private final static String WATER_MARK_LOCATION_X = "x";
    private final static String WATER_MARK_LOCATION_Y = "y";
    
/**
     * 添加文字水印
     *
     * @param inputStream      输入图片流
     * @param waterMarkContent 水印文字
     * @param location         水印位置: 1、左上角,2、右上角,3、左下角,4、右下角,5、中间
     * @param degree           旋转角度
     * @param alpha            水印透明度
     *
     * @return
     */
    public static InputStream buildWatermarkOfText(InputStream inputStream, String waterMarkContent,
                                                   WaterMarkLocation location,
                                                   Integer degree, float alpha) {

        try (ByteArrayOutputStream os = new ByteArrayOutputStream();) {
            Image srcImg = ImageIO.read(inputStream);
            if (srcImg != null) {
                //获取图片的宽
                int srcImgWidth = srcImg.getWidth(null);
                //获取图片的高
                int srcImgHeight = srcImg.getHeight(null);
                // 加水印
                BufferedImage bufImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage
                        .TYPE_INT_RGB);
                //获取Graphics2D
                Graphics2D g = bufImg.createGraphics();
                int fontSize = srcImgWidth < PIXEL_300 ? 1 * PIXEL_20 : srcImgWidth / PIXEL_300 * PIXEL_20;
                //水印字体
                Font font = new Font("Times", Font.BOLD, fontSize);
                //设置字体
                g.setFont(font);
                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g.drawImage(srcImg, 0, 0, srcImgWidth, srcImgHeight, null);
                if (null != degree) {
                    // 设置水印旋转
                    g.rotate(Math.toRadians(degree), (double) srcImgWidth / 2, (double) srcImgHeight / 2);
                }

                // 如果水印图片高或宽大于目标图片时做的处理,使水印宽或高等于目标图片的宽高,并且等比例缩放
                FontMetrics fontMetrics = g.getFontMetrics(font);
                Map<String, Integer> waterMarkMetrics =
                        getWaterMarkMetrics(fontMetrics, waterMarkContent, srcImgWidth, srcImgHeight);
                int newSyWidth = waterMarkMetrics.get(WATER_MARK_WIDTH);
                int newSyHeight = waterMarkMetrics.get(WATER_MARK_HEIGHT);
				//设置水印位置
                Map<String, Integer> waterMarkLocation =
                        getWaterMarkLocation(location, srcImgWidth, srcImgHeight, newSyWidth, newSyHeight);
                int x = waterMarkLocation.get(WATER_MARK_LOCATION_X);
                int y = waterMarkLocation.get(WATER_MARK_LOCATION_Y);
                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
                g.drawString(waterMarkContent, x, y);
                //设置文字水印阴影
                //设置背景色
                g.setBackground(Color.WHITE);
                g.setColor(Color.white);
                g.fillRect(x, y - newSyHeight + 10, newSyWidth, newSyHeight);
                //通过使用当前绘图表面的背景色进行填充来清除指定的矩形。
                g.clearRect(0, 0, x, y);
                g.setPaint(Color.black);
                g.drawString(waterMarkContent, x, y);
                g.dispose();
                ImageIO.write(bufImg, "jpg", os);
                bufImg.flush();
                InputStream input = new ByteArrayInputStream(os.toByteArray());
                return input;
            }

        } catch (IOException e) {
            e.printStackTrace();
        }//文件转化为图片
        catch (Exception e) {
            log.info("上传图片失败{}", e.getMessage());
        }
        return null;
    }

    /**
     * 获取水印宽高:处理水印超过原图片大小后,需要等比缩放水印大小
     *
     * @param fontMetrics      字体源数据
     * @param waterMarkContent 水印
     * @param srcWidth         原图片宽
     * @param srcHeight        原图片高
     *
     * @return
     */
    private static Map<String, Integer> getWaterMarkMetrics(FontMetrics fontMetrics, String waterMarkContent,
                                                            int srcWidth, int srcHeight) {
        Map<String, Integer> metricsMap = new HashMap<>();

        int waterMarkWidth = fontMetrics.stringWidth(waterMarkContent);
        int waterMarkHeight = fontMetrics.getHeight();
        int newSyWidth = waterMarkWidth;
        int newSyHeight = waterMarkHeight;

        if (waterMarkWidth > srcWidth) {
            newSyWidth = srcWidth;
            newSyHeight = (int) ((double) newSyWidth / waterMarkWidth * srcHeight);
        }

        if (newSyHeight > srcHeight) {
            newSyHeight = srcHeight;
            newSyWidth = (int) ((double) newSyHeight / waterMarkHeight * newSyWidth);
        }
        metricsMap.put(WATER_MARK_WIDTH, newSyWidth);
        metricsMap.put(WATER_MARK_HEIGHT, newSyHeight);
        return metricsMap;
    }

     /**
     * 获取水印位置
     *
     * @param location    水印位置: 1、左上角,2、右上角,3、左下角,4、右下角,5、中间
     * @param srcWidth    原图片宽度
     * @param srcHeight   原图片高度
     * @param newSyWidth  水印宽度
     * @param newSyHeight 水印高度
     *
     * @return
     */
    private static Map<String, Integer> getWaterMarkLocation(WaterMarkLocation location, int srcWidth, int srcHeight,
                                                             int newSyWidth,
                                                             int newSyHeight) {
        Map<String, Integer> locationMap = new HashMap<>();
        // 根据位置参数确定坐标位置
        int x = 0, y = 0;
        // location 位置: 1、左上角,2、右上角,3、左下角,4、右下角,5、中间
        switch (location.getLocation()) {
            case 1:
                break;
            case 2:
                x = srcWidth - newSyWidth;
                break;
            case 3:
                y = srcHeight - newSyHeight;
                break;
            case 4:
                x = srcWidth - newSyWidth;
                y = srcHeight - newSyHeight;
                break;
            case 5:
                x = (srcWidth - newSyWidth) / 2;
                y = (srcHeight - newSyHeight) / 2;
                break;
            default:
                break;
        }
        locationMap.put(WATER_MARK_LOCATION_X, x);
        locationMap.put(WATER_MARK_LOCATION_Y, y);
        return locationMap;
    }

     /**
     * 相对位置
     */
    public enum WaterMarkLocation {
        /**
         * 西北,左上角
         */
        NORTH_WEST(1),
        /**
         * 东北,右上角
         */
        NORTH_EAST(2),
        /**
         * 西南,左下角
         */
        SOUTH_WEST(3),
        /**
         * 东南,右下角
         */
        SOUTH_EAST(4),

        /**
         * 中部
         */
        CENTER(5);

        private Integer location;

        WaterMarkLocation(Integer location) {
            this.location = location;
        }

        public Integer getLocation() {
            return location;
        }

        public void setLocation(Integer location) {
            this.location = location;
        }
    }

6.加水印示例

给图片加时间水印,水印位置设置在左下角,设置0.5f的透明度,效果如本文开头的效果图。

/**
     * 加水印+上传
     *
     * @param file    文件
     *
     * @return 文件的访问地址
     */
    private static String uploadFile(MultipartFile file) {
       try {
            InputStream inputStream = file.getInputStream();
            //水印内容
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
            String waterMarkContent = sdf.format(new Date());
           //水印位置设置在左下角,设置0.5f的透明度
            inputStream = buildWatermarkOfText(inputStream, waterMarkContent, WaterMarkLocation.SOUTH_WEST, null, 0.5f);
           //TODO:调用上传
            return fileName;
        } catch (Exception e) {
            log.error("{}", "上传文件失败");
            throw new RuntimeException("上传文件失败");
        }
    }

四、上传

图片存储我们使用的是oss,先添加oss的maven依赖

        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.10.2</version>
        </dependency>

上传图片

@Slf4j
public class OSSUtil {
    //OSS 的地址
    private final static String OSS_END_POINT = "XXX";
    //OSS 的key值
    private final static String OSS_ACCESS_KEY_ID = "XXX";
    //OSS 的secret值
    private final static String OSS_ACCESS_KEY_SECRET = "XXX";
    //OSS 的bucket名字
    private final static String OSS_BUCKET_NAME = "XXX";
    //设置URL过期时间为10年
    private final static Date OSS_URL_EXPIRATION = DateUtils.addDays(new Date(), 365 * 10);

    // 300像素
    private final static Integer PIXEL_300 = 300;
    // 20像素
    private final static Integer PIXEL_20 = 20;

    private final static String WATER_MARK_WIDTH = "waterMarkWidth";
    private final static String WATER_MARK_HEIGHT = "waterMarkHeight";
    private final static String WATER_MARK_LOCATION_X = "x";
    private final static String WATER_MARK_LOCATION_Y = "y";
    private volatile static OSSClient instance;

    private OSSUtil() {
    }

    /**
     * 单例
     *
     * @return OSS工具类实例
     */
    private static OSSClient getOSSClient() {
        if (instance == null) {
            synchronized(OSSUtil.class) {
                if (instance == null) {
                    instance = new OSSClient(OSS_END_POINT, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET);
                }
            }
        }
        return instance;
    }

    /**
     * 当Bucket不存在时创建Bucket
     * <p>
     * Bucket命名规则:
     * 1.只能包含小写字母、数字和短横线,
     * 2.必须以小写字母和数字开头和结尾
     * 3.长度在3-63之间
     */
    private static void createBucket() {
        try {
            if (!OSSUtil.getOSSClient().doesBucketExist(OSS_BUCKET_NAME)) {//判断是否存在该Bucket,不存在时再重新创建
                OSSUtil.getOSSClient().createBucket(OSS_BUCKET_NAME);
            }
        } catch (Exception e) {
            log.error("{}", "创建Bucket失败,请核对Bucket名称(规则:只能包含小写字母、数字和短横线,必须以小写字母和数字开头和结尾,长度在3-63之间)");
            throw new RuntimeException("创建Bucket失败,请核对Bucket名称(规则:只能包含小写字母、数字和短横线,必须以小写字母和数字开头和结尾,长度在3-63之间)");
        }
    }
    
/**
     * 上传到OSS服务器  如果同名文件会覆盖服务器上的
     *
     * @param file    文件
     * @param fileDir 上传到OSS上文件的路径,例如test/
     *
     * @return 文件的访问地址
     */
    private static String uploadFile(MultipartFile file, FileDirType fileDir) {
        String fileName = String.format(
                "%s.%s",
                UUID.randomUUID().toString(),
                FilenameUtils.getExtension(file.getOriginalFilename()));
        try {
            InputStream inputStream = file.getInputStream();
            //水印内容
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
            String waterMarkContent = sdf.format(new Date());
            //添加水印
            inputStream = buildWatermarkOfText(inputStream, waterMarkContent, WaterMarkLocation.SOUTH_WEST, null, 0.5f);
            //创建上传Object的Metadata
            ObjectMetadata objectMetadata = new ObjectMetadata();
            objectMetadata.setContentLength(inputStream.available());
            objectMetadata.setCacheControl("no-cache");
            objectMetadata.setHeader("Pragma", "no-cache");
            objectMetadata.setContentType(getContentType(FilenameUtils.getExtension("." + file.getOriginalFilename())));
            objectMetadata.setContentDisposition("inline;filename=" + fileName);
            //上传文件
            PutObjectResult putResult = OSSUtil.getOSSClient()
                    .putObject(OSS_BUCKET_NAME, fileDir.getDir() + fileName, inputStream, objectMetadata);
                    //设置文件读写权限
            OSSUtil.getOSSClient().setObjectAcl(OSS_BUCKET_NAME, fileDir.getDir() + fileName,
                    CannedAccessControlList.PublicRead);
            return fileName;
        } catch (Exception e) {
            log.error("{}", "上传文件失败");
            throw new RuntimeException("上传文件失败");
        }
    }
    
    //文件路径的枚举
    public enum FileDirType {
        TEST("test/");
        private String dir;

        FileDirType(String dir) {
            this.dir = dir;
        }

        @JsonValue
        public String getDir() {
            return dir;
        }
    }
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
S-CMS电子商城系统(双语带手机版)以php+mysql进行开发,网站安装简单、快捷。程序特色:一、使搜索引擎更容易抓取和索引二、提供更多的功能,提高用户的友好体验三、可用性的提高,提高用户的友好体验使用方法:将文件上至PHP空间或服务器,运行http://你的域名/install.php进行安装。只需两步即可安装完成,为了保证网站安全,请修改默认后台路径及数据库名称。S-CMS电子商城系统系统(含微信小程序)v3.0 bulid20190507更新说明:1.新增:支持编辑器上图片水印及同步到OSS云储存2.新增:产品和新闻分类按钮启用click点击,采用a链接,对搜索引擎更友好3.新增:新增MIP(即移动端网页速技术)模板,可以有效的提高网站的搜索引擎排名S-CMS电子商城系统系统(含微信小程序)v3.0 bulid20190516更新说明:1.新增:支持编辑器上图片水印及同步到OSS云储存2.新增:产品和新闻分类按钮启用click点击,采用a链接,对搜索引擎更友好3.新增:新增MIP(即移动端网页速技术)模板,可以有效的提高网站的搜索引擎排名S-CMS电子商城系统系统(含微信小程序)v3.0 bulid20190522更新说明:1.修复:修复在部分PHP版本下万能表单系统无法删除和新增的问题2.新增:在安装时及在后台可以自定义数据表前缀,方便在同一数据库下安装多个网站S-CMS电子商城系统系统(含微信小程序)v3.0 bulid20190528更新说明:1.优化:网站采集功能优化,支持多页采集,多项目同时执行以及进度条显示2.新增:万能表单系统条目支持设置必填或者非必填,更灵活3.新增:万能表单系统新增审核通过与不通过两种选项,并且以邮件通知用户结果S-CMS电子商城系统系统(含微信小程序)v3.0 bulid20190604更新说明:1.优化:对支付部分的敏感入参数(如订单金额)进行了密,防止恶意篡改2.优化:对后台登录的二次验证进行优化,仅验证IP前三段,避免因开启CDN导致多次验证3.优化:后台自动数据备份仅保留最近十日,避免占用空间过大,手动备份不受影响S-CMS电子商城系统系统(含微信小程序)v3.0 bulid20190611更新说明:1.优化:后台选择上素材时入了名称显示和筛选排序功能2.修复:修复了在开启静态的状态下手机版分页功能显示错误的问题3.新增:入了自动备份的开关,无需自动备份的用户可以关闭该功能
阿里云OSS附件通允许将门户,论坛附件,图片,相册等上到阿里云OSS,该插件与Discuz无缝集成,取代Discuz论坛附件上,相册上,空间图片等功能,几乎完全支持原有附件的所有功能。使用阿里云存储,将大大减少本地论坛所需要的空间,将附件与论坛空间分离,减少费用。 同时附件通支持视频/音频附件的播放和视频两侧图片广告/视频类型广告。阿里云OSS附件通不仅仅是一款附件插件,而且是一款功能强大的音/视频插件,是搞音/视频论坛的站长的首选。 如果碰到安装上的问题,请查看文档安装文档 其他问题或升级问题,请查看插件页面上的常见问题文档 1. 支持远程附件的所有功能 2. 支持板块封面图片设置 3. 支持附件在远程和本地之间随意互相转换 4. 支持远程附件和本地附件的备份 5. 支持远程上功能的开启和关闭,附件下载不受影响。 6. 支持设置超过特定大小的附件才能被上到阿里云OSS 7. 支持门户,论坛,相册的所有附件操作。 8. 支持附件水印,缩略图,权限设置等功能 9. 强力支持音/视频附件功能,是做音/视频网站站长的首选。 10. 入反盗版检测,使用盗版的插件程序可能会导致论坛不能正常工作。如果出现此问题,概不负责。 2013.7.24 1. 增远程音/视频附件的播放功能。用户通过上音/视频附件立即就能播放。 2. 增Flowplayer播放器,可以专门播放flv格式视频。 3. 增FlowPlayer播放器的广告功能,支持在视频两侧设置广告位。类似土豆,优酷,迅雷的广告功能。 4. 支持设置视频开头广告。类似优酷,土豆,迅雷的功能。开播前播放30秒或更长的视频广告,并支持超链接,用于淘宝客,游戏等的推广,给站长的视频站点带去超强的盈利能力!! 5. 增远程附件域名绑定,使得远程附件可采用内链方式访问。 6. 后台增对空间图片/缩略图的同步功能 7. 优化插件性能,同步附件采用断点断续的方式。 8. 入反盗版检测,使用盗版的插件程序可能会导致论坛不能正常工作。如果出现此问题,概不负责。 9. 去除流量监控检测功能。 2013.7.18 1. 修正页面设置后可能出现空白的问题 2. 修正主题删除后没有删除远程附件的问题 3. 修正附件备份目录没有删除干净的问题 4. 增附件手动同步功能。 5. 增同步时断点续功能 2013.7.11 1. 增对X2,X2.5版本的支持 2. 增一系列高级附件操作功能 2013.7.5 1. 支持远程附件的所有功能 2. 强了Discuz原有的防盗链功能,附件连接使用直链方式,不消耗服务器资源。 3. 支持板块封面图片设置 4. 支持附件在远程和本地之间随意互相转换 5. 支持远程附件和本地附件的备份 6. 支持远程上功能的开启和关闭,附件下载不受影响。 7. 支持阿里云OSS的流量监控,防止超流量而使得产生费用。 8. 支持设置超过特定大小的附件才能被上到阿里云OSS 9. 支持门户,论坛,相册的所有附件操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值