excel 導(dǎo)出 base64 格式的圖片(可顯示多個(gè)圖片)

1碑幅、需求描述

首先系統(tǒng)中有錄入電子簽章的功能戴陡,錄入電子簽章可以通過(guò) web 端和 uniapp 端進(jìn)行錄入;錄入后在業(yè)務(wù)表中需要導(dǎo)出沟涨,導(dǎo)出時(shí)有可能要在一個(gè)單元格顯示多張圖片恤批。

2、實(shí)現(xiàn)思路

由于之前在 web 端和 app 端實(shí)現(xiàn)的電子簽章不是一個(gè)人做的裹赴,導(dǎo)致兩邊存的數(shù)據(jù)格式不一樣喜庞,web 端的存的是 svg 格式的base64 圖片诀浪,而uniapp 里面存的是 png 格式,導(dǎo)致導(dǎo)出的時(shí)候出現(xiàn)了很多問(wèn)題延都,我這里把web端的簽章改成了 png 格式雷猪,這樣導(dǎo)出方便許多


image.png

3、代碼

 /**
     * 導(dǎo)出處理電子簽章
     *
     * @param modelMap
     * @param templateExportParams
     * @param list
     * @param response
     */
    private void dealSign(ModelMap modelMap, TemplateExportParams templateExportParams, List<Entity> list, HttpServletResponse response) {
        OutputStream out = null;
        try {
            // 拿到模板文件
            String filePath = "your_excel_file";
            FileInputStream tps = new FileInputStream(new File(filePath));
            final HSSFWorkbook workbook = new HSSFWorkbook(tps);
            out = response.getOutputStream();
            response.reset();
            String fileName = "數(shù)據(jù)";
            fileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
            response.setHeader("content-disposition", "attachment;filename=" + fileName + ".xls");
            response.setContentType("APPLICATION/msexcel");
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

            HSSFSheet sheet = workbook.getSheetAt(0);
            HSSFCellStyle style = workbook.createCellStyle();
            style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
            style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
            //Font font = workbook.createFont();
            //font.setFontHeightInPoints((short) 12);
            //style.setFont(font);
            style.setWrapText(true);// 設(shè)置自動(dòng)換行
            // 全局唯一
            Drawing drawing = sheet.createDrawingPatriarch();
            for (int i = 0; i < list.size(); i++) {
                TbHiddenDangerHandleEntity t = list.get(i);
                Row row = sheet.createRow(i + 2); // 前兩行被標(biāo)題和字段占用
                // 自己獲取到 要用的電子簽章
                String[] signs= t.getSigns();

                if (signs != null) {
                    
                    // 在最后一列插入圖片
                    Cell rowCell = row.createCell(1);
                    rowCell.setCellStyle(style);

                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

                    BufferedImage bufferedImage = ImageUtil.base64ToBufferedImage(signs);

                    int width = bufferedImage.getWidth();
                    int height = bufferedImage.getHeight();
                    // 這里調(diào)整的寬高是自己測(cè)試的結(jié)果晰房,可能和我們的模板有關(guān)
                    sheet.setColumnWidth(1, width * 8 );
                    row.setHeightInPoints((short) height / 6);

                    ImageIO.write(bufferedImage, "png", outputStream);
                    anchor = new HSSFClientAnchor(0, 0, 1023, 255, (short) 1, i + 2, (short) 2, i + 3);
                    anchor.setAnchorType(ClientAnchor.DONT_MOVE_AND_RESIZE);
                    //畫(huà)圖的頂級(jí)管理器求摇,一個(gè)sheet只能獲取一個(gè)(一定要注意這點(diǎn))
                    Picture picture = drawing.createPicture(anchor, workbook.addPicture(outputStream.toByteArray(), Workbook.PICTURE_TYPE_PNG));

                    picture.resize(0.2);
                    //sheet.autoSizeColumn(25);

                }

            }
            HSSFWorkbook realWorkbook = workbook;
            realWorkbook.write(out);
        } catch (final Exception e) {
            e.printStackTrace();
            logger.error(e);
        } finally {
            try {
                response.flushBuffer();
                if (out != null) {
                    out.flush();
                    out.close();
                }
            } catch (final IOException e) {
                logger.error(e);
            }
        }
    }
  • 圖片處理工具類
import sun.misc.BASE64Encoder;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Base64;

/**
 * @author lucien
 * @date 2023/7/22
 * @dec 描述
 */
public class ImageUtil {
    /**
     * BufferedImage 編碼轉(zhuǎn)換為 base64
     * @param bufferedImage
     * @return
     */
    public static String BufferedImageToBase64(BufferedImage bufferedImage) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();//io流
        //String imageFormat = getImageFormat(bufferedImage);
        try {
            ImageIO.write(bufferedImage, "png", baos);//寫(xiě)入流中
        } catch (IOException e) {
            e.printStackTrace();
        }
        byte[] bytes = baos.toByteArray();//轉(zhuǎn)換成字節(jié)

        return "data:image/png;base64," + Base64.getEncoder().encodeToString(bytes);
    }

    // Method to get the image format (e.g., "png", "jpeg", "jpg") from a BufferedImage
    private static String getImageFormat(BufferedImage image) {
        for (String format : ImageIO.getWriterFormatNames()) {
            if (format.equalsIgnoreCase("png") || format.equalsIgnoreCase("jpeg") || format.equalsIgnoreCase("jpg")) {
                if (ImageIO.getImageWritersByFormatName(format).next().getDefaultWriteParam().canWriteCompressed()) {
                    return format;
                }
            }
        }
        return "png"; // Default to PNG format if no suitable format is found
    }



    /**
     * base64 編碼轉(zhuǎn)換為 BufferedImage
     * @param base64
     * @return
     */
    public  static BufferedImage base64ToBufferedImage(String base64) {
        String base64Image = "";
        try {
            // 棄用 svg
            //if(base64.contains("svg")){
            //    base64Image = base64.replace("image/svg+xml;base64,", "");
            //    //Base64.Decoder decoder = Base64.getDecoder();
            //    byte[] svgBytes = java.util.Base64.getDecoder().decode(base64Image);
            //    // Use Apache Batik to convert SVG to BufferedImage
            //    Transcoder transcoder = new PNGTranscoder();
            //    TranscoderInput transcoderInput = new TranscoderInput(new ByteArrayInputStream(svgBytes));
            //    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            //    TranscoderOutput transcoderOutput = new TranscoderOutput(outputStream);
            //    transcoder.transcode(transcoderInput, transcoderOutput);
            //
            //    // Convert the output stream to a BufferedImage
            //    ByteArrayInputStream imageInputStream = new ByteArrayInputStream(outputStream.toByteArray());
            //    return ImageIO.read(imageInputStream);
            //} else if(base64.contains("image/png;base64,")){
                base64Image = base64.replace("data:image/png;base64,", "").replace("image/png;base64,", "");
                // 這是從 app 錄入的簽章,先縮小
                byte[] bytes1 = java.util.Base64.getDecoder().decode(base64Image);
                ByteArrayInputStream bais = new ByteArrayInputStream(bytes1);
                BufferedImage read = ImageIO.read(bais);
                //return scale(read,0.3f);
                return read;
            //}


        } catch (IOException e) {
            e.printStackTrace();
        }
        //catch (TranscoderException e) {
        //    e.printStackTrace();
        //}
        return null;
    }

    /**
     * 將多個(gè) base64 圖片合并轉(zhuǎn)換為 BufferedImage
     * @param base64Str
     * @return
     */
    public  static BufferedImage base64ToBufferedImage(ArrayList<String> base64Str) {
        if(base64Str.size() == 0){
            return null;
        }else if(base64Str.size() == 1){
            return base64ToBufferedImage(base64Str.get(0));
        } else {
            try {
                return combineImages(base64Str);
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }

    }

    // Method to combine multiple images into a single image
    private static BufferedImage combineImages(ArrayList<String> base64Str) throws IOException {
        int totalWidth = 0;
        int maxHeight = 0;

        // Find the total width and maximum height of the images
        for (String imageBase64 : base64Str) {
            BufferedImage image = base64ToBufferedImage(imageBase64);
            totalWidth += image.getWidth();
            maxHeight = Math.max(maxHeight, image.getHeight());
        }

        // Create a new combined image with the calculated dimensions
        BufferedImage combinedImage = new BufferedImage(totalWidth, maxHeight, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = combinedImage.createGraphics();

        int xOffset = 0;
        for (String imageBase64 : base64Str) {
            BufferedImage image = base64ToBufferedImage(imageBase64);
            g.drawImage(image, xOffset, 0, null);
            xOffset += image.getWidth();
        }

        g.dispose();
        // 縮放一半
        //return scale(combinedImage,0.1f);
        return combinedImage;
    }

    // 縮放圖片嫉你,經(jīng)過(guò)測(cè)試月帝,有bug
    //public static BufferedImage scale(BufferedImage srcImageBuffer, float scale){
    //    //獲取縮放后的寬高
    //    int width = (int) (srcImageBuffer.getWidth()*scale);
    //    int height = (int) (srcImageBuffer.getHeight()*scale);
    //    //調(diào)用縮放方法獲取縮放后的圖片
    //    Image img = srcImageBuffer.getScaledInstance(width , height, Image.SCALE_DEFAULT);
    //    //創(chuàng)建一個(gè)新的緩存圖片
    //    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    //    //獲取畫(huà)筆
    //    Graphics2D graphics = image.createGraphics();
    //    graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    //    //將Image對(duì)象畫(huà)在畫(huà)布上,最后一個(gè)參數(shù),ImageObserver:接收有關(guān) Image 信息通知的異步更新接口,沒(méi)用到直接傳空
    //    graphics.drawImage(img, 0, 0,width,height,null);
    //
    //    //一定要釋放資源
    //    graphics.dispose();
    //    //獲取到文件的后綴名
    //    return image;
    //
    //}

}

  • 注意:如果一定要用 svg 格式的圖片,要用到 下面的 依賴來(lái)處理
            <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-transcoder</artifactId>
            <version>1.7</version>
        </dependency>
  • 最后的效果圖:


    image.png

4幽污、總結(jié)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末寺擂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子泼掠,更是在濱河造成了極大的恐慌怔软,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件择镇,死亡現(xiàn)場(chǎng)離奇詭異挡逼,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)腻豌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門家坎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人吝梅,你說(shuō)我怎么就攤上這事虱疏。” “怎么了憔涉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵订框,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我兜叨,道長(zhǎng)穿扳,這世上最難降的妖魔是什么衩侥? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮矛物,結(jié)果婚禮上茫死,老公的妹妹穿的比我還像新娘。我一直安慰自己履羞,他們只是感情好峦萎,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著忆首,像睡著了一般爱榔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上糙及,一...
    開(kāi)封第一講書(shū)人閱讀 52,268評(píng)論 1 309
  • 那天详幽,我揣著相機(jī)與錄音,去河邊找鬼浸锨。 笑死唇聘,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的柱搜。 我是一名探鬼主播迟郎,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼聪蘸!你這毒婦竟也來(lái)了宪肖?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤健爬,失蹤者是張志新(化名)和其女友劉穎匈庭,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體浑劳,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年夭拌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了魔熏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鸽扁,死狀恐怖蒜绽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情桶现,我是刑警寧澤躲雅,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站骡和,受9級(jí)特大地震影響相赁,放射性物質(zhì)發(fā)生泄漏相寇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一钮科、第九天 我趴在偏房一處隱蔽的房頂上張望唤衫。 院中可真熱鬧,春花似錦绵脯、人聲如沸佳励。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)赃承。三九已至,卻和暖如春悴侵,著一層夾襖步出監(jiān)牢的瞬間瞧剖,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工畜挨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留筒繁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓巴元,卻偏偏與公主長(zhǎng)得像毡咏,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子逮刨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容