Base64工具類及2點坑

   最近公司做疫情通行這塊,設備要求圖片傳base64字符串,就分享下這個工具類,以及裏面的一些坑吧,直接上碼。

package com.xiaotian.bus.util;

import com.easylinkin.bm.thirdapi.request.upload.UploadRequest;
import com.easylinkin.bm.thirdapi.response.ResponseData;
import com.easylinkin.bm.thirdapi.util.ThirdPartyServeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.io.IOUtils;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * base64文件工具類
 * @author zwsky
 */
@Slf4j
public class FileBase64Util {

    /**
     * 本地文件(圖片、excel等)轉換成Base64字符串
     *
     * @param imgPath
     */
    public static String convertFileToBase64(String imgPath) {
        byte[] data = null;
        // 讀取圖片字節數組
        try {
            InputStream in = new FileInputStream(imgPath);
            log.info("文件大小(字節)="+in.available());
            data = new byte[in.available()];
            in.read(data);
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 對字節數組進行Base64編碼,得到Base64編碼的字符串
        BASE64Encoder encoder = new BASE64Encoder();
        String base64Str = encoder.encode(data).replaceAll("[\\s*\t\n\r]", "");

        Integer base64Len = base64Str.length();
        log.info("----base64 length:{}",base64Len);

        return base64Str;
    }

    /**
     * 網絡地址文件轉爲base64字符串相關信息
     * @param netUrl 網絡地址
     * @return map對象
     * @throws Exception
     */
    public static Map<String,String> netUrlToBase64(String netUrl) throws Exception {
        Map<String,String> mp = null;
        URL url = new URL(netUrl);
        //將圖片文件轉化爲字節數組字符串,並對其進行Base64編碼處理
        log.info("圖片的路徑爲:" + url.toString());
        String fileType = netUrl.substring(netUrl.lastIndexOf("."));
        //打開鏈接
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) url.openConnection();
            //設置請求方式爲"GET"
            conn.setRequestMethod("GET");
            //超時響應時間爲5秒
            conn.setConnectTimeout(5 * 1000);
            //通過輸入流獲取圖片數據
            InputStream inStream = conn.getInputStream();
            //得到圖片的二進制數據,以二進制封裝得到數據,具有通用性
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            //創建一個Buffer字符串
            byte[] buffer = new byte[1024];
            //每次讀取的字符串長度,如果爲-1,代表全部讀取完畢
            int len = 0;
            //使用一個輸入流從buffer裏把數據讀取出來
            while ((len = inStream.read(buffer)) != -1) {
                //用輸出流往buffer裏寫入數據,中間參數代表從哪個位置開始讀,len代表讀取的長度
                outStream.write(buffer, 0, len);
            }
            int size1 = inStream.available();
            //關閉輸入流
            inStream.close();
            byte[] data = outStream.toByteArray();
            Long size = Long.parseLong(String.valueOf(outStream.size()));

            log.info("--outSize:{}----inSize:{}",size,size1);
            //對字節數組Base64編碼
            BASE64Encoder encoder = new BASE64Encoder();
            //根據RFC822規定,BASE64Encoder編碼每76個字符,還需要加上一個回車換行
            //部分Base64編碼的java庫還按照這個標準實行
            String base64 = encoder.encode(data).replaceAll("[\\s*\t\n\r]", "");
            log.info("網絡文件[{}]編碼成base64字符串:[{}]"+url.toString()+base64);
            //Integer base64Len = base64.length();

            //1.獲取base64字符串長度(不含data:audio/wav;base64,文件頭)
            Integer base64Len = base64.length();
/*
            //2.獲取字符串的尾巴的最後10個字符,用於判斷尾巴是否有等號,正常生成的base64文件'等號'不會超過4個
            String tail = base64.substring(base64Len - 10);

            //3.找到等號,把等號也去掉,(等號其實是空的意思,不能算在文件大小裏面)
            int equalIndex = tail.indexOf("=");
            if(equalIndex > 0) {
                base64Len = base64Len - (10 - equalIndex);
            }


            Integer base64Len = base64.length();
            int fileSize = base64Len-(base64Len/8)*2;
            log.info("----base64 length:{}",base64Len);


            //1.獲取base64字符串長度(不含data:audio/wav;base64,文件頭)
            int size0 = base64.length();

            //2.獲取字符串的尾巴的最後10個字符,用於判斷尾巴是否有等號,正常生成的base64文件'等號'不會超過4個
            String tail = base64.substring(size0-10);

            //3.找到等號,把等號也去掉,(等號其實是空的意思,不能算在文件大小裏面)
            int equalIndex = tail.indexOf("=");
            if(equalIndex > 0) {
                size0 = size0 - (10 - equalIndex);
            }

            //4.計算後得到的文件流大小,單位爲字節
            fileSize = (int) Math.round((size0 -( (double)size0 / 8 ) * 2));
*/

            //返回Base64編碼過的字節數組字符串
            mp = new HashMap<>(3);
            mp.put("fileType",fileType);
            mp.put("base64",base64);
            mp.put("size",String.valueOf(base64Len));
            //mp.put("size",String.valueOf(fileSize));
        } catch (IOException e) {
            e.printStackTrace();
            throw new Exception("圖片上傳失敗,請聯繫客服!");
        }finally {
            return mp;
        }
    }

    /**
     * 網絡文件轉base64字符串
     * @param netUrl 網絡url
     * @return base64字符串
     * @throws Exception
     */
    public static String netFileToBase64(String netUrl) throws Exception {
        URL url = new URL(netUrl);
        //將圖片文件轉化爲字節數組字符串,並對其進行Base64編碼處理
        log.info("圖片的路徑爲:" + url.toString());
        //打開鏈接
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) url.openConnection();
            //設置請求方式爲"GET"
            conn.setRequestMethod("GET");
            //超時響應時間爲5秒
            conn.setConnectTimeout(5 * 1000);
            //通過輸入流獲取圖片數據
            InputStream inStream = conn.getInputStream();
            //得到圖片的二進制數據,以二進制封裝得到數據,具有通用性
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            //創建一個Buffer字符串
            byte[] buffer = new byte[1024];
            //每次讀取的字符串長度,如果爲-1,代表全部讀取完畢
            int len = 0;
            //使用一個輸入流從buffer裏把數據讀取出來
            while ((len = inStream.read(buffer)) != -1) {
                //用輸出流往buffer裏寫入數據,中間參數代表從哪個位置開始讀,len代表讀取的長度
                outStream.write(buffer, 0, len);
            }
            //關閉輸入流
            inStream.close();
            byte[] data = outStream.toByteArray();
            //對字節數組Base64編碼
            BASE64Encoder encoder = new BASE64Encoder();
            String base64 = encoder.encode(data).replaceAll("[\\s*\t\n\r]", "");
            log.info("網絡文件[{}]編碼成base64字符串:[{}]"+url.toString()+base64);
            //返回Base64編碼過的字節數組字符串
            return base64;
        } catch (IOException e) {
            e.printStackTrace();
            throw new Exception("圖片上傳失敗,請聯繫客服!");
        }
    }


    /**
     * 將base64字符串,生成文件
     * @param fileBase64String base64文件字符串
     * @param filePath 路徑
     * @param fileName 文件名稱
     * @return 文件
     */
    public static File convertBase64ToFile(String fileBase64String, String filePath, String fileName) {
        BufferedOutputStream bos = null;
        FileOutputStream fos = null;
        File file = null;
        try {
            File dir = new File(filePath);
            //判斷文件目錄是否存在
            if (!dir.exists() && dir.isDirectory()) {
                dir.mkdirs();
            }

            BASE64Decoder decoder = new BASE64Decoder();
            byte[] bfile = decoder.decodeBuffer(fileBase64String);

            file = new File(filePath + File.separator + fileName);
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos);
            bos.write(bfile);
            return file;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) throws Exception {
        String filePath = "C:\\Users\\zwsky\\Desktop\\testImg\\22.jpg";
        String base64Str = FileBase64Util.convertFileToBase64(filePath);
        log.info("----base64Str:{}",base64Str);
        log.info("------");

        String localFilePath = "C:\\Users\\zwsky\\Desktop\\testImg";
        FileBase64Util.convertBase64ToFile(base64Str,localFilePath,"zwtest.JPG");
        log.info("------end---------");

        /**
        //String localFilePath = LocalHostUtil.getTmpDir();
        String localFilePath = "D:/application/bm/tmpDir";
        String netUrl = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1583992587635&di=df289355a4a4ae94c9ce6d2a229dd090&imgtype=0&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20180228%2F75e94470278748ad9132eb3443949e50.jpeg";
        Map<String,String> mp = FileBase64Util.netUrlToBase64(netUrl);
        log.info("--mp:{}",mp.toString());
        String netBase64Str = FileBase64Util.netFileToBase64(netUrl);
        log.info("-------:{}",netBase64Str);
        File testFile = FileBase64Util.convertBase64ToFile(netBase64Str,localFilePath,"netTest.jpeg");
         **/
         /**
 MultipartFile
        String strUrl = "F:\\測試圖片.jpg";
        File file = new File(strUrl);
        InputStream inputStream = new FileInputStream(file);
        MultipartFile multipartFile = new MockMultipartFile(testFile.getName(), testFile.getAbsolutePath(), null, inputStream);
        log.info("file轉multipartFile成功. {}",multipartFile);
 **/
/**

        InputStream inputStream = new FileInputStream(testFile);
        MultipartFile multipartFile = new MockMultipartFile(testFile.getName(), testFile.getAbsolutePath(), null, inputStream);
       **/
        /** CommonsMultipartFile
        //form表單文件控件的名字隨便起,//文件類型  ,//是否是表單字段,//原始文件名,//Interger的最大值可以存儲兩部1G的電影,//文件會在哪個目錄創建
        FileItem fileItem = new DiskFileItem("formFieldName",
                                        Files.probeContentType(testFile.toPath()),
                                        false,
                                        testFile.getName(),
                                        (int) testFile.length(),
                                        testFile.getParentFile());
        // 最關鍵的一步:爲DiskFileItem的OutputStream賦值
        // IOUtils是org.apache.commons.io.IOUtils;
        // 與此類似的還有FileUtils
        IOUtils.copy(new FileInputStream(testFile), fileItem.getOutputStream());
        MultipartFile cMultiFile = new CommonsMultipartFile(fileItem);
         **/
/**
        List<MultipartFile> fileList = new ArrayList<>();
        fileList.add(multipartFile);
        UploadRequest uploadRequest = new UploadRequest();
        uploadRequest.setObjectId("1");
        uploadRequest.setObjectType(1);
        uploadRequest.setUploadFile(fileList);

        ResponseData responseData = ThirdPartyServeUtil.uploadFile(uploadRequest);
**/
    }
}

坑點:

1、大小

   對接的文檔說傳圖片的base64字符串,還要傳大小。也沒說是這個圖片文件流的大小,還是其他什麼。害我一頓操作猛如虎,但是每次返回都是圖片大小不匹配。然後就考慮是不是本生這個圖片轉base64字符串後的文件大小,又考慮什麼轉換最後又=,不算大小等等,代碼裏面的沒有刪除,大家可以看看註釋,寫了好幾種方法求大小。

2、BASE64Encoder轉base64字符串有換行

查詢資料說:

根據RFC822規定,BASE64Encoder編碼每76個字符,還需要加上一個回車換行 
部分Base64編碼的java庫還按照這個標準實行

BASE64Encoder是JDK自帶的,我用的是1.8的jdk。那麼這裏可見對接方不一定用的自帶的,所以我得處理掉所有換行

基本上就這些,這裏再說一嘴,實際上不僅僅圖片可以轉base64字符串,其他文件也是可以。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章