最近公司做疫情通行這塊,設備要求圖片傳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字符串,其他文件也是可以。