赞
踩
无论是任何数据,在计算机的世界里本质上都是0101010 二进制字符流,而无论是ASCII、Unicode、UTF-8、Base64等编码方法本质上就是约定了对于二进制字符流的解释规则,解码程序/解释程序都遵守同一种解释方法,因而能洞悉二进制字符流的背后真正含义。
一个字节(8位)可表示的范围是0——255(即十六进制:0x00——0xFF),其中ASCII 码的值范围为0——127(即十六进制:0x00——0x7F),超过ASCII 码范围的(即十六进制:0x80——0xFF)之间的值是不可见的。
一个0或者1 二进制字符为一位;一个字节 8位;6位为一个Base64 单元;一个Base64单元对应一个可打印的字符
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nEHYuYUC-1604140903735)(D:\Doc\个人文档\TODO\assets\image-20200930095810077.png)]
ASCII(American Standard Code for Information Interchange,美国信息互换标准代码)是一套基于拉丁字母的字符编码,共收录了 128 个字符,用一个字节就可以存储,它等同于国际标准 ISO/IEC 646。
在ASCII码中规定,0--31
和127
共33个索引对应的字符属于控制字符(具有某些特殊功能但是无法显示的字符,不可打印字符,如下表所示部分常见的控制字符)
而32--126
共95个索引对应的字符属于可打印字符(可以显示,也可以从键盘上输入的字符),网络传输只能传输这95个字符,不在这个范围内的字符无法传输,比如一个纯文本协议,二进制中可能会出现被当做控制字符处理的数据,导致传输失败或者解析失败。
因为在设备之间通过网络通信时,往往要经过多个路由设备,由于不同的设备对字符的处理方式有一些不同(经ASCII/UTF-8编码后在大多数机器处理相同),这样那些不可见字符就有可能被处理错误,这是不利于传输的,而Base64 就是一种简洁统一的解释方案。
Base64是一种基于64个可打印
字符来表示二进制数据的编码方法常用于表示、传输、存储二进制数据,也可以用于将一些含有特殊字符的文本内容编码,以便传输。大部分网络协议(比如HTTP协议、SMTP、MQTT等)是纯文本协议,而在网络协议下二进制格式的数据(图片,音频,视频,语音等非文本字符)无法直接传输,但直接转换是不行的,因为网络传输只能传输可打印字符,故可以先将原始的二进制字节数组使用Base64 重新进行编码,使所有的字符都是可见的,再行传输。
Base64 里的64个字符在ASCII 码里都是可见的,64个字符中不包括特殊字符以免被一些应用认为是控制字符,如ftp,ssh等,Base64 不是加密算法,也不应该作为校验之用
标准 Base64 里的有 65 个可打印字符—— A--Z a--z 0--9 + / =
或者A--Z a--z 0--9 - _ =
(即小写字母a-z、大写字母A-Z、数字0-9、符号"+"("-")、"/"("_") 再加上作为垫字的"=")作为基本的编码字符集,分别依次对应索引值 0-63,Base64 编码就是使用这64个字符来解释二进制字节流,其他所有符号都转换成这个字符集中的字符。简而言之,Base64编码就是把所有字符变为可见的字符。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Im1ZbYRZ-1604140903747)(D:\Doc\个人文档\TODO\assets\image-20200930100815786.png)]
为了兼容不同的场景下Base64的应用,国际上有了两种字符集:
RFC 4648定义Base64的字符集
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 + /
RFC 4686提议使用另外一个字符集
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 - _
因为RFC 4648 字符集中包括了 ‘+/’ ,而一般的URL和文件名中,不能使用这两个字符,故RFC 4686 字符集诞生。
而具体用哪个字符集,要结合具体的场景。若只是在项目内部使用,编解码都可控,则就可以使用第二个,甚至可以使用一个自定义的字符集;但如果是将数据给第三方,那么使用第一个字符集更好,因为大部分库的实现都使用了第一个字符集,Base64相应的索引表:
如果不是ASCII 码值,需要在Base64编码前先转码,如可以直接转为二进制流。
转换过程针对不同的情况,具体步骤略有差异:
首先从ASCII码表得知M 对应的ASCII码十进制值为77,二进制表示为01001101,继续查阅a 、n 得到各自的二进制字节流,然后合并到一起,再从左到右按照每6位为一组(即一个Base64单元)得到全新的二进制字节流,再转为十进制值得到在Base64字节表的索引,于是乎Man 的Base64 编码变为TWFu,不过也由原来的3个字节变成了4个可打印字符,经Base64 编码后体积增加了1/3,即编码前后长度比4/3。
上面Man 编码长度3个字节24位,正好24/6=4个Base64,字节流长度刚好是6的倍数时。
当编码字节流长度不能被6整除时,在末尾会多出1个字节或者2个字节,所以需要使用0 来补足1 字节或者 2字节后,再重组为Base64单元。
再以以 Hello!!
为例,其转换过程为:http://blog.xiayf.cn/2016/01/24/base64-encoding/
Hello!!
经Base64编码后计算得到的为 SGVsbG8hIQAA
,但最后2个零值只是为了Base64编码而补充的,在原始字符中并没有对应的字符,即Base64编码结果中的最后两个字符 AA
实际上不带有效信息,所以需要特殊处理——标准Base64编码通常用 =
字符来替换最后的 A
,即最终编码结果为 SGVsbG8hIQ==
。
因为
=
字符并不在Base64编码索引表中,其意义在于结束符号,在Base64解码时遇到=
时即可知道一个Base64编码字符串结束。
但如果确认Base64编码字符串不会相互拼接再传输,那么最后的 =
也可以省略,解码时如果发现Base64编码字符串长度不能被4整除,则先补充 =
字符,再解码即可。
解码是对编码的逆向操作,对于最后的两个
=
字符,转换成两个A
字符,再转成对应的两个6比特二进制0值,接着转成原始字符之前,需要将最后的两个6比特二进制0值丢弃,因为它们实际上不携带有效信息。
Base64部分图文摘自一文读懂Base64编码。
MD5信息摘要算法(MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5码以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值,每次的运算都由前一轮的128位结果值和当前的512bit值进行运算。简而言之,就是通过MD5算法可以从字符串或者文件对象中提取唯一的一串固定长度为32的散列字符串
Log.e("CRAZYMO","获取图片路径对应文件的MD5="+BitmapUtil.MD5.getFileMD5(BitmapUtil.getSDPath()+"/cmo.PNG"));
Log.e("CRAZYMO","获取图片的Base64的MD5="+BitmapUtil.MD5.getStrMD5(BitmapUtil.bitmap2Base64(BitmapUtil.getSDPath()+"/cmo.PNG")));
Bitmap bitmap=BitmapFactory.decodeFile(BitmapUtil.getSDPath()+"/cmo.PNG");
Log.e("CRAZYMO","获取图片的bytes数组的MD5="+BitmapUtil.MD5.getBytesMD5(BitmapUtil.bitmap2Bytes(bitmap)));
import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Environment; import android.support.annotation.NonNull; import android.support.annotation.StringDef; import android.text.TextUtils; import android.util.Base64; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; public class BitmapUtil { public static String getSDPath() { boolean sdCardExist = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); if (sdCardExist) { return Environment.getExternalStorageDirectory().getPath(); } else { return ""; } } /** * 将图片转换成Base64编码的字符串 * * @param path Bitmap的全路径 */ public static String bitmap2Base64(String path) { if (TextUtils.isEmpty(path)) { return null; } InputStream is = null; byte[] data = null; String result = null; try { is = new FileInputStream(path); //创建一个字符流大小的数组。 data = new byte[is.available()]; //写入数组 is.read(data); //用默认的编码格式进行编码 result = Base64.encodeToString(data, Base64.NO_CLOSE); } catch (Exception e) { e.printStackTrace(); } finally { if (null != is) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } return result; } /** * 将Base64编码字符串转为图片 * * @param base64Str 源编码的Base64字符串 * @return bitmap */ public static Bitmap base64ToBitmap(String base64Str) { byte[] data = Base64.decode(base64Str, Base64.NO_WRAP); if (data != null && data.length > 0) { for (int i = 0; i < data.length; i++) { if (data[i] < 0) { //调整异常数据 data[i] += 256; } } return BitmapFactory.decodeByteArray(data, 0, data.length); } return null; } /** * 将Base64编码转换为Bitmap 并保存 * * @param base64Str 源编码的Base64字符串 * @param parentDir Bitmap保存的父目录路径 * @param name Bitmap 的名称,不需要带上后缀 * @return Bitmap的全路径名称 */ public static String base64ToBitmap(String base64Str, String parentDir, String name, @ImageType String type) { String path = ""; byte[] data = Base64.decode(base64Str, Base64.NO_WRAP); for (int i = 0; i < data.length; i++) { if (data[i] < 0) { //调整异常数据 data[i] += 256; } } OutputStream os = null; try { File parentFile = new File(parentDir); if (!parentFile.exists()) { parentFile.mkdirs(); } else { if (getFileSize(parentFile) > 1024 * 1024 * 50) { deleDir(parentDir); } } File jpgFile = new File(parentDir, name + type); os = new FileOutputStream(jpgFile); os.write(data); os.flush(); path = jpgFile.getAbsolutePath(); } catch (IOException e) { e.printStackTrace(); } finally { if (os != null) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } return path; } public static byte[] bitmap2Bytes(Bitmap bitmap) { ByteArrayOutputStream byteArrOutStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrOutStream); return byteArrOutStream.toByteArray(); } public static Bitmap bytes2Bitmap(@NonNull byte[] bytes) { if (bytes.length != 0) { return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); } return null; } public static Bitmap compressionBitmap(@NonNull Bitmap bitmap) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); if (baos.toByteArray().length / 1024 > 2048) { baos.reset(); bitmap.compress(Bitmap.CompressFormat.JPEG, 60, baos); } ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray()); BitmapFactory.Options newOpts = new BitmapFactory.Options(); newOpts.inJustDecodeBounds = true; BitmapFactory.decodeStream(isBm, null, newOpts); newOpts.inJustDecodeBounds = false; int w = newOpts.outWidth; int h = newOpts.outHeight; int be = 1; if (w > h && ((float) w) > 1280.0f) { be = (int) (((float) newOpts.outWidth) / 1280.0f); } else if (w < h && ((float) h) > 720.0f) { be = (int) (((float) newOpts.outHeight) / 720.0f); } if (be <= 0) { be = 1; } newOpts.inSampleSize = be; newOpts.inPreferredConfig = Bitmap.Config.RGB_565; return BitmapFactory.decodeStream(new ByteArrayInputStream(baos.toByteArray()), null, newOpts); } /** * 将原始的位图数据的bytes 保存到指定路径下 * * @param data * @param parentDir Bitmap保存的父目录路径 * @param name Bitmap 的名称,不需要带上后缀 * @return Bitmap的全路径名称 */ public static void save2SDCard(byte[] data, String parentDir, String name, @ImageType String type) { FileOutputStream outputStream = null; SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA); String dateStr = df.format(new Date()); String photoName = "qr_" + dateStr; try { File parentFile = new File(parentDir); if (!parentFile.exists()) { parentFile.mkdirs(); } File jpgFile = new File(parentDir, photoName + type); outputStream = new FileOutputStream(jpgFile); // 文件输出流 // BASE64Decoder decoder = new BASE64Decoder(); // try { // // Base64解码 // byte[] bytes = decoder.decodeBuffer(imgStr.substring(22)); // // System.out.println("bytes的长度:"+bytes.length); // // for (int i = 0; i < bytes.length; ++i) { // if (bytes[i] < 0) {// 调整异常数据 // bytes[i] += 256; // } // } // // 生成jpg图片 // OutputStream out = new FileOutputStream(imgFilePath+imgFileName); // out.write(bytes); // out.flush(); // out.close(); // } catch (Exception e) { // // } outputStream.write(data); // 写入sd卡中 } catch (IOException e) { e.printStackTrace(); } finally { try { if (outputStream != null) { outputStream.close(); // 关闭输出流 } } catch (IOException e) { e.printStackTrace(); } } } public static boolean fileExits(String fileName) { return (fileName == null || "".equals(fileName) || !new File(fileName).exists()) ? false : true; } public static boolean deleteFile(String fileName) { File file = new File(fileName); if (fileExits(fileName)) { return file.delete(); } return false; } public static boolean deleDir(String dirName) { if (!fileExits(dirName)) { return false; } File dir = new File(dirName); File[] tmp = dir.listFiles(); for (int i = 0; i < tmp.length; i++) { if (tmp[i].isDirectory()) { deleDir(dirName + "/" + tmp[i].getName()); } else { tmp[i].delete(); } } return dir.delete(); } public static long getFileSize(File f) { long size = 0; File flist[] = f.listFiles(); for (int i = 0; i < flist.length; i++) { if (flist[i].isDirectory()) { size = size + getFileSize(flist[i]); } else { size = size + flist[i].length(); } } return size; } public static class MD5{ /** * 根据图片路径获取MD5 * @param imagePath * @return */ public static String getFileMD5(String imagePath) { String ret=""; if (!TextUtils.isEmpty(imagePath)) { File file = new File(imagePath); if(file.exists()) { InputStream in=null; StringBuilder md5 = new StringBuilder(); try { MessageDigest md = MessageDigest.getInstance("MD5"); in= new FileInputStream(file); int n = 0; byte[] dataBytes = new byte[1024]; while ((n = in.read(dataBytes)) != -1) { md.update(dataBytes, 0, n); } byte[] bytes = md.digest(); for (int i = 0; i < bytes.length; i++) { md5.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1)); } ret=md5.toString().toUpperCase(); } catch (NoSuchAlgorithmException | IOException e) { e.printStackTrace(); } finally { if(in!=null){ try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } } } return ret; } /** * 获取字符串对应的MD5 * @param str * @return */ public static String getStrMD5(String str) { String ret=""; if (!TextUtils.isEmpty(str)) { try { MessageDigest md5 = MessageDigest.getInstance("MD5"); char[] charArray = str.toCharArray(); byte[] byteArray = new byte[charArray.length]; for (int i = 0; i < charArray.length; i++) { byteArray[i] = (byte) charArray[i]; } byte[] md5Bytes = md5.digest(byteArray); StringBuilder hexValue = new StringBuilder(); for (int i = 0; i < md5Bytes.length; i++) { int val = ((int) md5Bytes[i]) & 0xff; if (val < 16) { hexValue.append("0"); } hexValue.append(Integer.toHexString(val)); } ret= hexValue.toString().toUpperCase(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } return ret; } } /** * 获取Bytes 数组的MD5 * @param charArray * @return */ public static String getBytesMD5(byte[] charArray){ String ret=""; if(charArray!=null && charArray.length>0){ try { MessageDigest md5 = MessageDigest.getInstance("MD5"); byte[] byteArray = new byte[charArray.length]; for (int i = 0; i < charArray.length; i++) { byteArray[i] = charArray[i]; } byte[] md5Bytes = md5.digest(byteArray); StringBuilder hexValue = new StringBuilder(); for (int i = 0; i < md5Bytes.length; i++) { int val = ((int) md5Bytes[i]) & 0xff; if (val < 16) { hexValue.append("0"); } hexValue.append(Integer.toHexString(val)); } ret = hexValue.toString().toUpperCase(); }catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } return ret; } @StringDef({ImageType.JPG, ImageType.PNG, ImageType.BITMAP}) @Retention(RetentionPolicy.SOURCE) public @interface ImageType { String PNG = ".png"; String JPG = ".jpg"; String BITMAP = ".bmp"; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。