最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot集成FFmpeg實現(xiàn)生成圖片預(yù)覽圖與縮略圖

 更新時間:2026年02月13日 09:40:55   作者:god_cvz  
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何集成FFmpeg實現(xiàn)生成圖片預(yù)覽圖與縮略圖,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以參考一下

本文介紹了SpringBoot集成FFmpeg生成圖片預(yù)覽圖與縮略圖功能的實現(xiàn)方案。首先配置Maven依賴,包含JavaCV核心庫和FFmpeg等組件。核心類PicturePreviewProcessor實現(xiàn)了從URL獲取圖片、生成不同尺寸預(yù)覽圖(720×540)、縮略圖(200×150)和WebP格式圖片的功能。通過Java2DFrameConverter和FFmpegFrameRecorder實現(xiàn)圖片格式轉(zhuǎn)換,提供圖片縮放計算方法getChangeSize()保持原圖比例。建議使用線程池異步處理圖片生成任務(wù),并自行實現(xiàn)OSS文件上傳邏

一、導(dǎo)入pom依賴

    <profiles>
        <profile>
            <id>local</id>
            <properties>
                <profiles.active>local</profiles.active>
                <!--根據(jù)環(huán)境進(jìn)行切換-->
                <ffmpeg.classifier>windows-x86_64</ffmpeg.classifier>
<!--                <ffmpeg.classifier>linux-x86_64</ffmpeg.classifier>-->
            </properties>
        </profile>
    </profiles> 
        <!-- javacv+javacpp核心庫-->
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv</artifactId>
            <version>${javacv.version}</version>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacpp-platform</artifactId>
            <version>${javacv.version}</version>
        </dependency>

        <!-- ffmpeg最小依賴包,必須包含上面的javacv+javacpp核心庫 -->
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>ffmpeg</artifactId>
            <version>5.1.2-${javacv.version}</version>
            <classifier>${ffmpeg.classifier}</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacpp</artifactId>
            <version>${javacv.version}</version>
            <classifier>${ffmpeg.classifier}</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>openblas</artifactId>
            <version>0.3.21-${javacv.version}</version>
            <classifier>${ffmpeg.classifier}</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>opencv</artifactId>
            <version>4.6.0-${javacv.version}</version>
            <classifier>${ffmpeg.classifier}</classifier>
        </dependency>

二、預(yù)覽圖生成代碼

package xxxx;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.UUID;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.springframework.stereotype.Component;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * 圖片預(yù)覽圖處理器
 *
 * @author god_cvz
 */
@Component
@Slf4j
public class PicturePreviewProcessor {

    /**
     * 異步執(zhí)行,防止阻礙主流程,建議使用線程池
     *
     * @param pictureUrl
     */
    public void asyncProcess(String pictureUrl) {
        new Thread(() -> process(pictureUrl)).start();
    }

    public void process(String pictureUrl) {
        log.info("開始處理照片 pictureUrl: {}", pictureUrl);
        try (InputStream inputStream = getInputStreamFromUrl(pictureUrl)) {
            // 安全地重置一個輸入流以重新讀取圖像數(shù)據(jù),若流不支持重置,則從網(wǎng)絡(luò)重新下載圖像?,確保圖像處理流程的可靠性
            if (inputStream.markSupported()) {
                // 將流指針歸位,復(fù)用原流,避免重復(fù)下載,提升效率與節(jié)省帶寬
                inputStream.reset();
                processImage(inputStream);
            } else {
                try (InputStream newInputStream = getInputStreamFromUrl(pictureUrl)) {
                    processImage(newInputStream);
                }
            }
        } catch (Exception e) {
            log.error("處理照片失敗: {}", e.getMessage());
        }
    }

    /**
     * 從指定 URL 下載資源并返回 InputStream
     *
     * @param urlString 資源的 URL 地址
     * @return InputStream (需要調(diào)用方手動關(guān)閉)
     * @throws IOException 下載或連接失敗時拋出異常
     */
    public InputStream getInputStreamFromUrl(String urlString) throws Exception {
        if (urlString == null || urlString.isEmpty()) {
            throw new IllegalArgumentException("URL 不能為空");
        }
        log.info("[DownloadUtils] 開始下載文件:{}", urlString);
        long startTime = System.currentTimeMillis();

        try {
            URL url = new URL(urlString);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(10_000);
            connection.setReadTimeout(15_000);
            connection.setDoInput(true);
            // 檢查響應(yīng)碼
            int responseCode = connection.getResponseCode();
            if (responseCode != HttpURLConnection.HTTP_OK) {
                throw new IOException("下載失敗,HTTP 響應(yīng)碼:" + responseCode);
            }
            log.info("[DownloadUtils] 連接成功[{}],開始接收數(shù)據(jù)...", url);
            InputStream inputStream = connection.getInputStream();
            long endTime = System.currentTimeMillis();
            log.info("[DownloadUtils] 下載完成,用時 {} ms", (endTime - startTime));
            return inputStream;
        } catch (Exception e) {
            log.error("[DownloadUtils] 下載文件失敗[{}], error:{}", urlString, e.getMessage());
            throw new Exception("[DownloadUtils] 下載文件失敗: " + e.getMessage());
        }
    }

    private void processImage(InputStream inputStream) throws IOException {
        BufferedImage sourceImage = ImageIO.read(inputStream);
        // 生成預(yù)覽圖
        generatePreview(sourceImage);
        // 生成縮略圖
        generateThumbnail(sourceImage);
        // 生成webp縮略圖
        generateWebp(sourceImage);
    }

    private void generatePreview(BufferedImage sourceImage) {
        // 預(yù)覽圖寬度
        int previewWidth = 720;
        // 預(yù)覽圖高度
        int previewHeight = 540;
        String previewFileName = UUID.randomUUID() + ".jpg";
        String objectKey = "/bucket/preview/" + previewFileName;
        uploadImage(sourceImage, previewWidth, previewHeight, objectKey, previewFileName);
    }

    private void generateThumbnail(BufferedImage sourceImage) {
        // 縮略圖寬度
        int thumbnailWidth = 200;
        // 縮略圖高度
        int thumbnailHeight = 150;
        String thumbnailFileName = UUID.randomUUID() + ".jpg";
        String objectKey = "/bucket/thumbnail/" + thumbnailFileName;
        uploadImage(sourceImage, thumbnailWidth, thumbnailHeight, objectKey, thumbnailFileName);
    }

    private void uploadImage(BufferedImage sourceImage, Integer targetWith, Integer targetHeight, String objectKey, String fileName) {
        File outputFile = new File(fileName);
        FileInputStream fileInputStream = null;
        try {
            int[] changeSize = this.getChangeSize(sourceImage.getWidth(), sourceImage.getHeight(), targetWith, targetHeight);
            // 創(chuàng)建目標(biāo)文件緩沖區(qū)
            BufferedImage outputImage = this.resizeImage(sourceImage, changeSize[0], changeSize[1]);
            // 保存圖片到文件
            ImageIO.write(outputImage, "jpg", outputFile);
            fileInputStream = new FileInputStream(outputFile);
            // 上傳至oss
            this.uploadFile(fileInputStream, objectKey);
            log.info("生成圖片成功: objectKey: {}", objectKey);
        } catch (Exception e) {
            log.error("生成圖片失敗: objectKey: {}, 原因: {}", objectKey, e.getMessage());
        } finally {
            IoUtil.closeIfPosible(fileInputStream);
            FileUtil.del(outputFile);
        }
    }

    /**
     * 計算圖像 按比例縮放后的寬高
     * 選擇寬高縮放后較大的一邊,固定其長度,另一邊按原圖比例縮放
     *
     * @param originalWidth  圖像原寬度
     * @param originalHeight 圖像原長度
     * @param targetWidth    目標(biāo)寬度
     * @param targetHeight   目標(biāo)高度
     * @return int[]
     */
    public int[] getChangeSize(int originalWidth, int originalHeight, int targetWidth, int targetHeight) {
        // 設(shè)置縮放比例
        double scale = Math.min(targetWidth / (double) originalWidth, targetHeight / (double) originalHeight);
        // 計算縮放后的寬高
        return new int[]{(int) (originalWidth * scale), (int) (originalHeight * scale)};
    }

    /**
     * 修改圖片尺寸
     *
     * @param originImage  原圖文件
     * @param targetWidth  目標(biāo)寬度
     * @param targetHeight 目標(biāo)高度
     * @return 制定尺寸圖片
     */
    public BufferedImage resizeImage(BufferedImage originImage, int targetWidth, int targetHeight) {
        BufferedImage resizedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = resizedImage.createGraphics();
        graphics.drawImage(originImage, 0, 0, targetWidth, targetHeight, null);
        graphics.dispose();
        return resizedImage;
    }

    /**
     * todo:自定義上傳至OSS文件存儲服務(wù)器
     *
     * @param fileInputStream 文件流
     * @param objectKey       OSS存儲key
     */
    private void uploadFile(FileInputStream fileInputStream, String objectKey) {

    }

    private void generateWebp(BufferedImage sourceImage) {
        String webpFileName = UUID.randomUUID() + ".webp";
        String objectKey = "/bucket/webp/" + webpFileName;
        uploadWebp(sourceImage, objectKey, webpFileName);
    }

    private void uploadWebp(BufferedImage sourceImage, String objectKey, String filename) {
        // 將BufferedImage轉(zhuǎn)換為Frame
        Java2DFrameConverter converter = new Java2DFrameConverter();
        Frame frame = converter.convert(sourceImage);
        File outputFile = new File(filename);
        FileInputStream inputStream = null;
        // 設(shè)置輸出圖像的參數(shù)
        try (FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, frame.imageWidth, frame.imageHeight)) {
            recorder.setFormat("webp");
            // 記錄frame
            recorder.start();
            recorder.record(frame);
            recorder.stop();
            inputStream = new FileInputStream(outputFile);
            this.uploadFile(inputStream, objectKey);
            log.info("生成webp成功: objectKey: {}", objectKey);
        } catch (Exception | Error e) {
            log.error("生成webp失敗: objectKey: {} - 原因: {}", objectKey, e.getMessage());
        } finally {
            IoUtil.closeIfPosible(inputStream);
            IoUtil.closeIfPosible(converter);
            FileUtil.del(outputFile);
        }
    }
}

tips:

1.建議使用線程池進(jìn)行異步處理

2.代碼中可自行修改預(yù)覽圖縮略圖寬高

3.文件上傳邏輯各自處理

到此這篇關(guān)于SpringBoot集成FFmpeg實現(xiàn)生成圖片預(yù)覽圖與縮略圖的文章就介紹到這了,更多相關(guān)SpringBoot FFmpeg生成圖片預(yù)覽圖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot jpa實現(xiàn)優(yōu)雅處理isDelete的默認(rèn)值

    springboot jpa實現(xiàn)優(yōu)雅處理isDelete的默認(rèn)值

    如果多個實體類都有 isDelete 字段,并且你希望在插入時為它們統(tǒng)一設(shè)置默認(rèn)值時改怎么做呢,本文為大家整理了一些方法,希望對大家有所幫助
    2024-11-11
  • 總結(jié)Junit4,Junit5,Jupiter之間的聯(lián)系

    總結(jié)Junit4,Junit5,Jupiter之間的聯(lián)系

    Jupiter和Junit5之間有什么聯(lián)系?Jupiter提供了哪些新的測試方法?如何用IDEA和Jupiter生成可讀性更好的測試報告?文中有非常詳細(xì)的說明,需要的朋友可以參考下
    2021-06-06
  • Java Web十條開發(fā)實用小知識

    Java Web十條開發(fā)實用小知識

    這篇文章主要介紹了Java Web十條開發(fā)實用小知識的相關(guān)資料,需要的朋友可以參考下
    2016-05-05
  • SpringBoot整合Flowable實現(xiàn)工作流的詳細(xì)流程

    SpringBoot整合Flowable實現(xiàn)工作流的詳細(xì)流程

    Flowable是一個使用Java編寫的輕量級業(yè)務(wù)流程引擎,Flowable流程引擎可用于部署B(yǎng)PMN2.0流程定義,創(chuàng)建這些流程定義的流程實例,進(jìn)行查詢,訪問運(yùn)行中或歷史的流程實例與相關(guān)數(shù)據(jù),本文給大家介紹了如何使用SpringBoot整合Flowable快速實現(xiàn)工作流,需要的朋友可以參考下
    2025-06-06
  • 二叉樹基本操作之遞歸和非遞歸遍歷、分支節(jié)點數(shù)詳解

    二叉樹基本操作之遞歸和非遞歸遍歷、分支節(jié)點數(shù)詳解

    這篇文章主要介紹了二叉樹基本操作之遞歸和非遞歸遍歷、分支節(jié)點數(shù)詳解,二叉樹是由n(n>=0)個結(jié)點的有限集合構(gòu)成,此集合或者為空集,或者由一個根結(jié)點及兩棵互不相交的左右子樹組成,并且左右子樹都是二叉樹,需要的朋友可以參考下
    2023-09-09
  • 使用Swagger直接上傳文件的方法

    使用Swagger直接上傳文件的方法

    這篇文章主要介紹了使用Swagger直接上傳文件的方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • 在Java中使用MongoDB的方法詳解

    在Java中使用MongoDB的方法詳解

    這篇文章主要給大家介紹了關(guān)于在Java中使用MongoDB的相關(guān)資料,要操作MongoDB數(shù)據(jù)庫你需要使用MongoDB的Java驅(qū)動程序,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-12-12
  • 在Spring Boot應(yīng)用程序中使用Apache Kafka的方法步驟詳解

    在Spring Boot應(yīng)用程序中使用Apache Kafka的方法步驟詳解

    這篇文章主要介紹了在Spring Boot應(yīng)用程序中使用Apache Kafka的方法步驟詳解,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-11-11
  • Java SpringBoot 使用攔截器作為權(quán)限控制的實現(xiàn)方法

    Java SpringBoot 使用攔截器作為權(quán)限控制的實現(xiàn)方法

    這篇文章主要介紹了Java SpringBoot 使用攔截器作為權(quán)限控制的實現(xiàn),文中通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-10-10
  • SpringMVC框架如何與Junit整合看這個就夠了

    SpringMVC框架如何與Junit整合看這個就夠了

    這篇文章主要介紹了SpringMVC框架如何與Junit整合看這個就夠了,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05

最新評論

安陆市| 汾西县| 嘉荫县| 陵水| 临高县| 伊春市| 克山县| 鸡东县| 梁山县| 体育| 丁青县| 岳池县| 响水县| 长子县| 察哈| 斗六市| 洪江市| 吉林市| 五峰| 呼伦贝尔市| 康保县| 荥阳市| 繁昌县| 临澧县| 深水埗区| 平泉县| 遂平县| 房山区| 自治县| 新疆| 乡城县| 天全县| 班玛县| 鄢陵县| 会昌县| 灵宝市| 灌南县| 长汀县| 唐海县| 兴化市| 东乌珠穆沁旗|