1.Java中直接使用spring框架提供的工具包来实现,Web页面生成的Base64正常解码,但是Android客户端生成的Base64解码报错,错误信息为:Illegal base64 character a, data=null]。
spring 原生框架提供jar将base64转图片核心代码如下:
byte[] bs = Base64Utils.decodeFromString(data);
try{
//使用apache提供的工具类操作流
FileUtils.writeByteArrayToFile(new File("D:", tempFileName), bs);
}catch(Exception ee){
throw new Exception("上传失败,写入文件失败,"+ee.getMessage());
}
2.Android客户端提交的Base64使用BASE64Decoder包生成的图片是破损的,原因是可能是没有拼接前缀data:image/jpeg;base64,或者是上传的过程中数据发生了改变。通过浏览器上http://imgbase64.duoshitong.com/,生成base64如下:
生成图片的base64可知,base6前拼接data:image/jpeg;base64,来标识这个base64应该生成什么类型的文件,所以客户端生成的base64也有拼上这段标识,而后台接口接收到数据时先要截取,根据标识去生成文件。
找了半天找到BASE64Decoder真是太happy了,然而访问到生成图片的代码时却报classNotFoundException,找不到这个包的类,但包明明已经导入到里面去了,编译能通过,但是运行到对应的类就报错了。原因是:通过右键项目Build Path —>Configure Build Path… —>Add External JARS 这样导入的jar包 只存在工作环境当中,当项目部署到tomcat以后,webapp文件夹下的项目文件夹中并不会存在我们的jar包。解决这个问题的方式有两种方法:
1、在WEB-INF下建一个文件夹 lib,把jar包 copy 进去,这样在部署项目的时候就会将jar也部署进去了。
2、将jar导入maven中,生成对应的pom版本。
将jar导入maven的指令:
./mvn install:install-file -Dfile="/Users/linxz/Desktop/jar/sun.misc.BASE64Decoder.jar" -DgroupId=sun.misc.base64decoder -DartifactId=sun.misc.base64decoder -Dversion=1.0.0 -Dpackaging=jar
如果是windows系统,则不要前面的./。
/Users/linxz/Desktop/jar/sun.misc.BASE64Decoder.jar为jar包的位置,DgroupId为项目组ID,DartifactId为项目ID,我这里皆用包名命名,Dversion为当前生成的版本号。这里就有点像android里生成aar部署到Nexus Maven仓库。Nexus搭建Maven私服
执行指令结果如下则表示打包成功:
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] --- maven-install-plugin:2.4:install-file (default-cli) @ standalone-pom ---
[INFO] Installing /Users/linxz/Desktop/jar/sun.misc.BASE64Decoder.jar to /Users/linxz/java/localmaven/sun/misc/base64decoder/sun.misc.base64decoder/1.0.0/sun.misc.base64decoder-1.0.0.jar
[INFO] Installing /var/folders/q9/ysm4v_fx14110jhts8dcw5s00000gn/T/mvninstall3804221028910250225.pom to /Users/linxz/java/localmaven/sun/misc/base64decoder/sun.misc.base64decoder/1.0.0/sun.misc.base64decoder-1.0.0.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.290 s
[INFO] Finished at: 2019-10-12T08:38:08+08:00
[INFO] ------------------------------------------------------------------------
linxzdeMacBook-Pro:bin linxz$
Installing /Users/linxz/Desktop/jar/sun.misc.BASE64Decoder.jar to /Users/linxz/java/localmaven/sun/misc/base64decoder为生成的包在我本地maven的路径,可以去该路径下查看是否真的存在:
这时候我们可以在代码中通过maven的方式导入:
<dependency>
<groupId>sun.misc.base64decoder</groupId>
<artifactId>sun.misc.base64decoder</artifactId>
<version>1.0.0</version>
</dependency>
public static String getBase64FromFilePath(String path){
InputStream inputStream = null;
byte[] data = null;
try {
inputStream = new FileInputStream(path);
data = new byte[inputStream.available()];
inputStream.read(data);
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
// 加密
BASE64Encoder encoder = new BASE64Encoder();
String base64=encoder.encode(data);
base64="data:image/jpeg;base64,"+base64;
return base64;
}
核心代码;
public static boolean base64ToImage(String imgStr,String imgFilePath) {
if (StringUtils.isEmpty(imgStr))
return false;
File file =new File(imgFilePath);
File fileParent=file.getParentFile();
if(!fileParent.exists()) {
fileParent.mkdirs();
}
BASE64Decoder decoder = new BASE64Decoder();
try {
byte[] b = decoder.decodeBuffer(imgStr);
for (int i = 0; i < b.length; ++i) {
if (b[i] < 0) {
// 调整异常数据
b[i] += 256;
}
}
OutputStream out = new FileOutputStream(imgFilePath);
out.write(b);
out.flush();
out.close();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
将传输过程中发生改变的字符替换回来:
base64=base64.replaceAll(" ","+");
base64=base64.replaceAll("[\\s*\t\n\r]", "");
文件路径不存在则创建对应文件夹:
File file =new File(imgFilePath);
File fileParent=file.getParentFile();
if(!fileParent.exists()) {
fileParent.mkdirs();
}
我项目中的业务处理方式:验证base64非空->验证用户token->验证当前用户状态时候可用->将传输过程中被修改的base64替换回来->根据拼接的前缀判断当前base64属于什么类型文件->判断生成文件的地址路径是否存在不存在则生成对应的路径文件夹->base64开始转文件。
@Override
public LinxzResult upLoadImg(FileParams fileParams) {
try {
if (StringUtils.isEmpty(fileParams.getBase64())) {
return LinxzResult.build(ResponseCodeUtils.CODE_PARAMS_EMPTY, "长传失败,上传图片不能为空");
}
//校验用户
String token = fileParams.getToken();
if(StringUtils.isEmpty(token)){
return LinxzResult.build(ResponseCodeUtils.CODE_PERMISSION_ERRO, "token错误");
}
String userId=TokenUtils.getUserId(token);
BaseUser user=baseUserMapper.selectByUserId(userId);
if(user==null || !token.equals(user.getToken())){
return LinxzResult.build(ResponseCodeUtils.CODE_PERMISSION_ERRO, "token错误");
}
if(!UserConfig.USER_STATUS_NORMAL.equals(user.getStattus())){
return LinxzResult.build(ResponseCodeUtils.CODE_PERMISSION_ERRO, "用户异常");
}
String base64=fileParams.getBase64();
base64=base64.replaceAll(" ","+");
base64=base64.replaceAll("[\\s*\t\n\r]", "");
System.out.println("上传文件的数据:" + base64);
String dataPrix = "";
String data = "";
String[] d = base64.split("base64,");
if (d != null && d.length == 2) {
dataPrix = d[0];
data = d[1];
} else {
return LinxzResult.build(ResponseCodeUtils.CODE_UNKNOWED_ERRO, "上传失败,数据不合法");
}
String suffix = "";
if ("data:image/jpeg;".equalsIgnoreCase(dataPrix)) {
// data:image/jpeg;base64,base64编码的jpeg图片数据
suffix = ".jpg";
} else if ("data:image/x-icon;".equalsIgnoreCase(dataPrix)) {
// data:image/x-icon;base64,base64编码的icon图片数据
suffix = ".ico";
} else if ("data:image/gif;".equalsIgnoreCase(dataPrix)) {
// data:image/gif;base64,base64编码的gif图片数据
suffix = ".gif";
} else if ("data:image/png;".equalsIgnoreCase(dataPrix)) {
// data:image/png;base64,base64编码的png图片数据
suffix = ".png";
} else {
return LinxzResult.build(ResponseCodeUtils.CODE_UNKNOWED_ERRO, "上传图片格式不合法");
}
String tempFileName = getRandomFileName() + suffix;
String homePath = "/Users/linxz/Pictures/streamlet/";
String folderName="";
if (!StringUtils.isEmpty(fileParams.getFolderName())) {
folderName = fileParams.getFolderName();
}
homePath = homePath+folderName+"/"+user.getUserId()+"/"+tempFileName;
boolean success=Base64Util.base64ToImage(data, homePath);
if(success) {
System.out.println("生成文件名为:" + homePath);
return LinxzResult.build(ResponseCodeUtils.CODE_SUCCESS, "长传成功", folderName + "/" + user.getUserId()+"/"+tempFileName);
}else {
return LinxzResult.build(ResponseCodeUtils.CODE_UNKNOWED_ERRO, "上传失败,写入文件失败");
}
} catch (Exception e) {
return LinxzResult.build(ResponseCodeUtils.CODE_UNKNOWED_ERRO, "长传失败" + e.getMessage());
}
}
之前一直觉得,开发接口之后postman请求没问题就证明接口是可用的,剩下的就是客户端的问题,这个图片上传使用Srping的包,postman请求网页生成的base64没问题,可是android原生bas4工具类生成的base64就是死活解码失败,改变了我接口对接的一些看法。