buguge - Keep it simple,stupid

知识就是力量,但更重要的,是运用知识的能力why buguge?

导航

java实现图片文件与Base64的互转

通过form表单上传图片时,有时候web容器对文件大小的限制会影响我们上传。这时,前端页面可以考虑将图片转换成base64串来实现上传。可参见:vue利用canvas将图片上传到服务器

 

■ 图片与Base64的互转,其实就是利用了文件字节流与Base64的互转

 

> 文件转换成Base64字符串:读取文件的输入流,因为文件流是字节流,所以要放到byte数组(字节数组,byte取值范围-128~127)里,然后对byte数组做Base64编码,返回字符串。

> Base64串转换成文件:对Base64编码的字符串进行Base64解码,得到byte数组,利用文件输出流将byte数据写入到文件。

 

介绍一下byte:   

byte,即字节,就是我们常说的B(b)。byte由8位二进制组成,在java中,byte类型的数据是8位带符号的二进制数。

在计算机中,8位带符号二进制数的取值范围是[10000000,01111111],其中高位(第一位)代表符号,0代表正数,1代表负数,10000000对应十进制-128,01111111对应十进制127,所以,byte的取值范围是[-128,127]。

现在的文件比如手机拍摄的照片都比较大,几乎不会谈到B了,也很少谈到K(kb)了,常见的是M(mb)。换算单位:1Mb = 1024kb, 1Kb=1024b。

byte数组就是字节数组,byte[] buffer=new byte[1024] 代表数组容量为1k。

 

 

字节(byte)是计算机中数据存储、传输和表示的基本单位。具体表现在:

 

1. 存储数据:计算机内存是以字节为单位进行存储的。所有的数据,包括数字、字符、图像、音频和视频等,都以字节的形式在内存中存储。

 

2. 数据传输:在计算机网络和存储设备之间传输数据时,常常以字节的形式进行传输。例如,通过网络传输文件、发送电子邮件、进行网络通信等都是以字节流的形式进行的。

 

3. 数据表示:字节可以表示不同的数据类型和编码方式。例如,一个字节可以表示一个ASCII字符,多个字节可以表示一个Unicode字符。字节还可以表示整数、浮点数和其他数据类型。

 

4. 文件操作:在文件系统中,文件的大小通常以字节为单位进行计量。读取和写入文件时,常常以字节流的形式进行操作。

 

 

Talk is cheap, show me the code。直接上代码:

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import java.io.*;

public class ImageBase64Converter {
    /**
     * 本地文件(图片、excel等)转换成Base64字符串
     *
     * @param imgPath     
     */
    public static String convertFileToBase64(String imgPath) {
        byte[] data = null;
        // 读取图片字节数组
        try {
            InputStream in = new FileInputStream(imgPath);
            System.out.println("文件大小(字节)="+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);
        return base64Str;
    }

    /**
     * 将base64字符串,生成文件
     */
    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();
                }
            }
        }
    }

}

 

testcase:

public static void main(String[] args) {
        long start = System.currentTimeMillis();
        String imgBase64Str= ImageBase64Converter.convertFileToBase64("D:\\Pictures\\科技\\liziqi-李子柒爆红.jpg");
//        System.out.println("本地图片转换Base64:" + imgBase64Str);  //是一个base64字符串:如 iVBORw0KGgoAAAANSUhEUgAAA8cAAAKVCAYA..........gMMLjLy/BMfDf3xNkAABAgQIbCogON6UU2d7CIz8430PL30SuEZg5P0lOL7mk6AuAQIECBAg8E/WacCDdkP4CwAAAABJRU5ErkJggg==
        System.out.println("Base64字符串length="+imgBase64Str.length());
        ImageBase64Converter.convertBase64ToFile(imgBase64Str,"D:\\Pictures\\科技","test.jpg");
        System.out.println("duration:"+(System.currentTimeMillis()-start));

        start=System.currentTimeMillis();
        String fileBase64Str= ImageBase64Converter.convertFileToBase64("D:\\Pictures\\科技\\PayOrderList200109075516581.xlsx");
//        System.out.println("本地excel转换Base64:" + fileBase64Str);
        System.out.println("Base64字符串length="+fileBase64Str.length());
        ImageBase64Converter.convertBase64ToFile(fileBase64Str,"D:\\Pictures\\科技","test.xlsx");
        System.out.println("duration:"+(System.currentTimeMillis()-start));

    }

 

执行结果:

文件大小(字节)=2820811
Base64字符串length=3860058
duration:244
文件大小(字节)=25506
Base64字符串length=34902
duration:10

 

 

提醒一下:获取文件的大小是用FileInputStream实例的available()方法哦,用File实例的length()返回的是0。

如下图,测试方法里图片文件“liziqi-李子柒爆红.jpg”的大小正是2820811字节   ÷1024=2755KB  ÷1024=2.68M

 

■ 关于 以base64编码的图像数据URL

一个以base64编码的图像数据URL由两个主要部分组成:数据类型和数据本身。

  • 数据类型:数据类型部分指定了图像的媒体类型(MIME类型),通常以"data:"开头,后跟媒体类型和编码方式。例如,对于GIF图像,数据类型部分可能是"data:image/gif;base64,"。
  • 数据本身:数据本身是经过base64编码的图像数据。它紧随数据类型部分,并作为URL的一部分。这部分数据是一串字符,表示二进制图像数据的编码形式。

下面是一个示例以base64编码的图像数据URL的结构:
data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
在这个例子中,数据类型部分是"data:image/gif;base64,",后跟base64编码后的图像数据。

此串放到web浏览器地址栏里可以直接访问,是一张极小的gif图片。在Vue.js框架的项目中,为了减少http请求,更快地渲染页面,vue-cli创建的webpack模板默认会将10k以下的图片和字体文件转换为base64。下图示例中是一个809b的网警图标。

 

 

网易126邮箱的记事本里,我们在富文本editor里Ctrl+C粘贴一张截图。 源码是:<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA8cAAAKVCAYA..........gMMLjLy/BMfDf3xNkAABAgQIbCogON6UU2d7CIz8430PL30SuEZg5P0lOL7mk6AuAQIECBAg8E/WacCDdkP4CwAAAABJRU5ErkJggg==" alt="">

这个img的src对应的字符串就是一个以base64编码的GIF图像数据的URL。它包含了一个图像文件的二进制数据。这种编码方式将图像数据转换成一串字符,以便在文本环境中传输或嵌入。

要处理这个编码的图像数据,我们需要使用相应的编程代码或工具来解码它。不同的编程语言和框架提供了不同的方法来解码base64编码的数据,并将其转换回原始的图像格式(如GIF、JPEG或PNG)。

以下是一个Python示例代码,演示如何解码base64数据并将其保存为图像文件:

import base64
import io
from PIL import Image

# 假设您的base64数据URL保存在变量base64_data中
base64_data = "data:image/gif;base64,..."

# 获取base64编码的图像数据部分
image_data = base64_data.split(",")[1]

# 解码base64数据
decoded_data = base64.b64decode(image_data)

# 将解码后的数据读取为图像对象
image = Image.open(io.BytesIO(decoded_data))

# 保存图像为文件
image.save("decoded_image.gif")

 

PS:博客园写随笔上传图片时,系统会先将图片上传到服务器,然后将url返回给前端。

 

 

 

 

■ Fastdfs工具类中的图片上传

FastdfsClientUtil中关于图片上传,有3个重载方法。

  • public String uploadImageAndCrtThumbImage(MultipartFile myfile) throws Exception
  • public String uploadImageAndCrtThumbImageByStream(InputStream inputStream,int size,String fileExtName) throws Exception
  • public String uploadImageAndCrtThumbImageByBase64(String base64ImgData) throws Exception

在这里,我说一下uploadImageAndCrtThumbImageByBase64这个方法,它的入参是一个base64编码的图像数据URL。这个参数的值是,在base64串的前头有一个MIME前缀,诸如 "data:image/gif;base64,"、"data:image/jpeg;base64,"。其中gif、jpeg是目标图片文件的扩展名。这正适合于前端页面通过base64串来上传图片的场景。

下面贴出来这个方法的实现,一看便知。

import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.exception.FdfsUnsupportStorePathException;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;

import java.io.ByteArrayInputStream;
import java.io.InputStream;

@Component
public class FastdfsClientUtil {

   private final Logger logger = LoggerFactory.getLogger(FastdfsClientUtil.class);
   @Autowired
   private FastFileStorageClient storageClient;

   //上传文件并生成缩略图
   public String uploadImageAndCrtThumbImageByBase64(String base64ImgData) throws Exception {

      BASE64Decoder decoder = new BASE64Decoder();
      String[] base64ImageSplit = base64ImgData.split(",");
      byte[] result = decoder.decodeBuffer(base64ImageSplit[1]);//解码
      String fileExtName = base64ImageSplit[0].substring(base64ImageSplit[0].indexOf("/")+1,base64ImageSplit[0].indexOf(";"));
      for (int i = 0; i < result.length; ++i) {
         if (result[i] < 0) {// 调整异常数据
            result[i] += 256;
         }
      }
      InputStream inputStream  = new ByteArrayInputStream(result);
      return uploadImageAndCrtThumbImageByStream(inputStream, result.length,fileExtName );
   }

}

 

测试用例:

    @Test
    public void uploadPng() throws Exception {

        String imgBase64="/9j/4AAQSkZJRgABAQEAYABgAAD/7QjCUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA\n" +
                "AgBIAAAAAQACOEJJTQQNAAAAAAAEAAAAHjhCSU0EGQAAAAAABAAAAB44QklNA/MAAAAAAAkAAAAA\n" +
                "AAAAAAEAOEJJTQQKAAAAAAABAAA4QklNJxAAAAAAAAoAAQAAAAAAAAACOEJJTQP1AAAAAABIAC9m\n" +
                "ZgABAGxmZgAGAAAAAAABAC9mZgABAKGZmgAGAAAAAAABADIAAAABAFoAAAAGAAAAAAABADUAAAAB\n" +
                "AC0AAAAGAAAAAAABOEJJTQP4AAAAAABwAAD/////////////////////////////A+gAAAAA////\n" +
                "/////////////////////////wPoAAAAAP////////////////////////////8D6AAAAAD/////\n" +
                ......
                "0HoaKACg80GjvQISijrmjFMD/9k=";
        String fileName = fastdfsClientUtil.uploadImageAndCrtThumbImageByBase64("data:image/png;base64,"+imgBase64);
        System.out.println("------------" + fileName);
        //fileName:上传后的文件(含路径):group1/M00/00/20/wKgoVF8hHdSAaRrkAAAlClwx2Hg883.png
        //访问:http://192.168.40.84:8888/group1/M00/00/20/wKgoVF8hHdSAaRrkAAAlClwx2Hg883.png

    }

 

 

 【EOF】知识就是力量,但更重要的是..., 欢迎关注我的微信公众号「靠谱的程序员」,解密靠谱程序员的日常,让我们一起做靠谱的程序员。

posted on 2020-01-10 19:59  buguge  阅读(44892)  评论(1编辑  收藏  举报