导航:首页 > 操作系统 > android二进制转图片

android二进制转图片

发布时间:2024-08-12 10:14:10

1. android 怎么用二进制流上传图片与音频文件

QusetionForm qusetionForm = (QusetionForm)form; FormFile imageTopic= qusetionForm.getImageTopic(); String name=imageTopic.getFileName(); String url=(servlet.getServletConfig().getServletContext().getRealPath("\\")+name).replace('\\','/');

2. android 图片转BASE64上传提示java.lang.OutOfMemoryError

我最近也碰到了这个问题,但是网上没有找到相关有效的直接解决方法。
后来看到了一篇解释base64编码原理的文章,研究了一番后解决了。
一般碰到这个问题的,都涉及到"大文件上传"的问题,"大文件上传"过程中除了base64编码时可能OOM,其实还有其他问题,虽然提问中没有提出,可能是因为这个问题还没有解决,所以还没有遇到其它问题,我就围绕着"大文件上传"来解决这个问题吧。
(提问时间在下看的清楚)

————————————————————

做项目的过程中碰到一个需求:
在java客户端,使用http通信,把客户端的本地文件通过http发送上传到服务器;

请求格式是xml(不管是json还是xml都是字符串,所以这个无所谓),中间包含[文件流字符串];

之前的做法是,把文件流通过base64编码转换为base64Byte,然后和其它字符串信息放到一起,post的时候通过HttpURLConnection的write方法写入到服务器中去,这个上传的过程就完成了。

——————————
但是碰到一个问题,当文件体积较大时,从文件流转换成base64Byte后,体积会很大,可能会导致OOM;
(以二进制流的方式保存,体积最小;以byte数组的方式保存,体积会相对变大一些;以String形式保存,体积最大;)

出错原因是:
FileInputStream fis = new FileInputStream(file); //这一步打开了一个对准file准备进行读取的文件指针,但是还没有开始读写,file的相关数据没有从本地加载到内存中来;所以即使file的体积有10G那么大,这一步也是不会OOM的

//把文件流转换为字节数组
byte[] fileBytes;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] byteBuf = new byte[1024];
int count;
while((count=fis.read(buf))!=-1)
{
baos.write(buf,0,count); //实际上,如果文件体积比较大的话,不用转码,在这一步就可能OOM了
}
fileBytes= baos.toByteArray();

byte[] base64Bytes = Base64.encodeBase64(fileBytes); //在这一步也可能OOM
(文件转换为byte[]时,是有可能OOM的;而转换为base64Bytes后,体积会增大1/3,所以有可能前一步没有OOM,却在这一步出现OOM;
为什么转码后体积会增大1/3,后面我会解释)

——————————
解决方法

既然file在本地没有加载到内存来的时候不会出现内存溢出的情况,我就想到了一个解决的方法:分段上传
(加大内存并不能从根本上解决内存溢出的问题,问题的根本原因不是内存不够大,而是代码有问题)

在本地的file通过HttpURLConnection的getOutputStream()进行write时,不是一次性全部写入,而是循环配合flush进行写入:
FileInputStream fis = new FileInputStream(file);
byte[] buf = new byte[1024];
int count;
while((count = fis.read(buf)) != -1)
{
os.write(Base64.encodeBase64(buf), 0, count);
os.flush();
}
(我从本地读1024字节,然后马上上传到服务器,清空本地缓存,然后再从本地读1024字节,这样循环读取,即使文件有20G,理论上也不有OOM问题出现,因为我从本地文件中读到的数据不会在内存中驻留)

——————————
解决问题的思路对了,但是出现了其他的细节问题
os.write(Base64.encodeBase64(buf), 0, count); //这一行代码报错了,出现了OOM
我搜集了一下资料,发现原因是:
HttpURLConnection的getOutputStream的实际对象是sun.net.<a href="http://www.http.PosterOutputStream" target="_blank">www.http.PosterOutputStream</a>,这个对象的flush方法代码是空的,write配合flush,并没有达到即时上传数据的效果。PosterOutputStream其实是自己在本地维护了一个缓冲区,你通过write写入的数据其实还是在这个本地的缓冲区里,只有当你getInputStream后,HttpURLConnection才会把这段缓冲区中的数据上传到服务器上。而flush达不到上传数据,清空本地缓存的效果。

——————————
(我是不能通过getInputStream来刷新缓冲流的,因为那就不是分段上传而是"分次"上传了)
那这就不是我的思路的问题了。再去搜索解决方法后,得知:
在创建HttpURLConnection对象的时候,要调用一个方法
hurlc.setChunkedStreamingMode(1024); //设置分块流模式 也就是分块上传 1024是getOutputStream维护的本地缓冲区的大小
调用该方法后,只要本地缓存区满了,HttpURLConnection就会自动把缓冲区里的数据发送到服务器,同时清空本地缓存(ps:HttpURLConnection的getOutputStream似乎是个抽象的工厂方法,在调用setChunkedStreamingMode方法后,我发现getOutputStream获取到的实例对象从sun.net.<a href="http://www.http.PosterOutputStream" target="_blank">www.http.PosterOutputStream</a>变成了sun.net.<a href="http://www.protocol.http.HttpURLConnection$StreamingOutputStream" target="_blank">www.protocol.http.HttpURLConnection$StreamingOutputStream</a>)

——————————
果然,调用setChunkedStreamingMode方法后,os.write(Base64.encodeBase64(buf), 0, count);没有再出现OOM异常了

但是,又出现了一个新的问题
我发现
FileInputStream fis = new FileInputStream(file);
byte[] buf = new byte[1024];
int count;
while((count = fis.read(buf)) != -1)
{
os.write(Base64.encodeBase64(buf), 0, count);
os.flush();
}
这段分段编码写入的代码,其编码所得结果,与非分段编码所得结果是不一样的
通过分段编码上传的图片内容出现了错误

我通过下面代码测试:
//分段编码
ByteArrayOutputStream os1 = new ByteArrayOutputStream();
InputStream file1 = new FileInputStream(path);
byte[] buf1 = new byte[1024];
int count1;
while((count1 = file1.read(buf1)) != -1)
{
os1.write(Base64.encodeBase64(buf1), 0, count1);
os1.flush();
}
file1.close();
System.out.println(os1.toString());

//非分段编码
ByteArrayOutputStream os2 = new ByteArrayOutputStream();
InputStream file2 = new FileInputStream(path);
byte[] buf2 = new byte[1024];
int count2;
while((count2 = file2.read(buf2)) != -1)
{
os2.write(buf2, 0, count2);
os2.flush();
}
file2.close();
System.out.println(new String(Base64.encodeBase64(os2.toByteArray())));

两者的结果:
/9j/4AAQSkZJR...wDtUAVs7eF...
/9j/4AAQSkZJR...wDt89ymnxJ...
前面一段还是相同的,转到后面,就开始南辕北辙了

——————————
原因我去网上找了一下但是没有找到直接答案,但是看到一篇解释base64编码原理的文章
原文链接:<a href="http://www.cnblogs.com/luguo3000/p/3940197.html" target="_blank">http://www.cnblogs.com/luguo3000/p/3940197.html</a>

假设有文件A(txt文件,包含文本内容"ABCDEFG"),转换为InputStream->byte[]后
它们的ASIIC码分别对应65、66、67、68、69、70、71
二进制表现形式为:
1000001 1000010 1000011 1000100 1000101 1000110 1000111
对高位补零后:
01000001 01000010 01000011 01000100 01000101 01000110 01000111
在内存中的实际表现:


而base64编码,使用的字符包括(A-Z、a-z、0-9、+、/、=)这些常规可读字符,使用base64编码的原因,用途,在于把一些乱码字符、不可读字符转换为常规可读字符;
(因为java底层的通信协议、或者说其它的通信协议,很多地方用到远程通信这一块的,对一些乱码字符不支持传输,所以需要把乱码字符转换成常规可读字符才能进行传输)

比如对于'矙'这个字符,部分传输协议的编码集就不认识它,所以无法直接传输,必须base64转码
'矙'的UTF-8编码值为30681,二进制表现形式为111011111011001->(0)111011111011001
需要两个字节来存储01110111 11011001

base64编码只有(A-Z、a-z、0-9、+、/、=)这些字符来表示。需要强调的是,在base64编码规范中,字符'A'不等于65、'B'也不是66...。base64字符与数值(二进制值)的对应关系如下:
也就是说,常规字符'A'=65=01000001;而base64字符'A'=0=00000000;

base64字符代表的二进制值是无法直接表示'矙'这个字符的,因为base64字符的值范围在0~63之间(二进制值在(00)000000~(00)111111之间)。
那如何通过(00)000000~(00)111111之间的数值来表示01110111 11011001呢?

这就是base64的编码算法
一个base64字符的二进制值在(00)000000~(00)111111之间,也就是说它可以表示000000~111111之间的二进制数,一个base64字符的有效位为后6位。如何通过以6bit为单位的base64字符表示以8bit为单位的常规字节?
6和8的最小公倍数为24,即 每4个base64字符可以表示3个常规字节;

回到刚才的文件A,编码过程:
(初始文件A)->"ABCDEFG"
(转UTF-8码 int)->65 66 67 68 69 70 71
("ABCDEFG"的二进制表示;7字节)->1000001 1000010 1000011 1000100 1000101 1000110 1000111
(高位补零)->01000001 01000010 01000011 01000100 01000101 01000110 01000111
(连写)->
(按6bit为单位对所有bit进行分割;得到10字节)->010000 010100 001001 000011 010001 000100 010101 000110 010001 11
(按6bit*4=8bit*3的对应关系再分割;得到3组6*4字节)->(010000 010100 001001 000011) (010001 000100 010101 000110) (010001 11)
(高位补2个零;末尾的低位也补零)->(00010000 00010100 00001001 00000011) (00010001 00000100 00010101 00000110) (00010001 00110000)
(二进制值换算成十进制)->(16 20 9 3) (17 4 21 6) (17 48)
(按base64编码的值-字符对应表,得出上面的十进制值对应的base64字符)->(Q U J D) (R E V G) (R w)
(每组base64字符都要求是4个,空白的位置补'='字符)->(Q U J D) (R E V G) (R w = =)
(文件A的最终转码结果)->QUJDREVGRw==
这里以文本文件作为演示,因为文本文件机器可读人也可读;实际情况中,很多时候转码的目标文件并不是文本文件,那就不能以可读字符串形式表示了,会直接以二进制格式表示

体积增大的原因,是因为3字节=24bit=分割成4个6bit-,对4个6bit高位补零后,就得到4个字节
也就是说3个常规字节经base64编码后会生成4个base64字节,这就是文件经base64转码后体积会增加1/3的原因

——————————
base64编码原理解释了,再看刚才的分段编码
ByteArrayOutputStream os1 = new ByteArrayOutputStream();
InputStream file1 = new FileInputStream(path);
byte[] buf1 = new byte[1024];
int count1;
while((count1 = file1.read(buf1)) != -1)
{
os1.write(Base64.encodeBase64(buf1), 0, count1); //可以发现一个问题:Base64.encodeBase64(buf1)编码后,体积会增加1/3,所以这里的Base64.encodeBase64(buf1)编码转换后的实际长度和count1并不相等,所以实际写入到os1中的base64字符数只有Base64.encodeBase64(buf1)编码产生的字符数的3/4
os1.flush();
}
file1.close();
System.out.println(os1.toString());

修改后:
ByteArrayOutputStream os1 = new ByteArrayOutputStream();
InputStream file1 = new FileInputStream(path);
byte[] byteBuf = new byte[1024];
byte[] base64ByteBuf;
while(file1.read(byteBuf) != -1)
{
base64ByteBuf = Base64.encodeBase64(byteBuf);
os1.write(base64ByteBuf, 0, base64ByteBuf.length);
os1.flush();
}
file1.close();
System.out.println(os1.toString());

——————————
修改后,发现分段编码的结果发生了变化,跟之前不一样了
但仍然不是正确的结果

原因在于,base64字符的基础单位是(6bit*4=4字节),而3个常规字节(8bit*3)才能刚好产生4个base64字节
根本原因在于,如果进行编码的常规字节数不是3的倍数,最后就会余下1或2个字节,而这1、2个字节编码的结果就会产生'='字符;
使用1024作为分段编码缓冲时,编码的结果是3+3+3+...+1
也就是每次都会余1字节

而没有使用分段编码时,当编码到第1024个字节时,"余下"的1字节会跟后面的字节形成连续,就不会产生'='字符
(对一段byte字符进行base64编码时,中间是绝不会产生'='字符的,因为只有在结尾才可能余下1或2个字节,所以对一段byte字符进行编码时,只有结尾才可能产生1或2个'='补全字符)

——————————
解决方法是,使用3的公倍数作为缓冲区大小

修改后:
ByteArrayOutputStream os1 = new ByteArrayOutputStream();
InputStream file1 = new FileInputStream(path);
byte[] byteBuf = new byte[3*1000];
byte[] base64ByteBuf;
while(file1.read(byteBuf) != -1)
{
base64ByteBuf = Base64.encodeBase64(byteBuf);
os1.write(base64ByteBuf, 0, base64ByteBuf.length);
os1.flush();
}
file1.close();
System.out.println(os1.toString());
测试结果再次发生了改变
中间不再有'='字符了,因为中间每次都是3字节3字节的编码,没有余下多余的字节
对比之后发现,中间段的结果已经正常了

——————————
但是,发现,结尾处两个转码的结果有些许不同

原因在于,假设文件A的长度为3001个字节;
在第二次循环读取时,只读到1个有效字节,而byteBuf的剩余2999个字节都是无效字节,而此时编码时,却把多余的2999个无效字节也编码了进去
(如果是非分段转码,就不会出现这种情况)

解决方法:
ByteArrayOutputStream os1 = new ByteArrayOutputStream();
InputStream file1 = new FileInputStream(path);
byte[] byteBuf = new byte[3*1000];
byte[] base64ByteBuf;
int count1; //每次从文件中读取到的有效字节数
while((count1=file1.read(byteBuf)) != -1)
{
if(count1!=byteBuf.length) //如果有效字节数不为3*1000,则说明文件已经读到尾了,不够填充满byteBuf了
{
byte[] = Arrays.Of(byteBuf, count1); //从byteBuf中截取包含有效字节数的字节段
base64ByteBuf = Base64.encodeBase64(); //对有效字节段进行编码
}
else
{
base64ByteBuf = Base64.encodeBase64(byteBuf);
}

os1.write(base64ByteBuf, 0, base64ByteBuf.length);
os1.flush();
}
file1.close();
System.out.println(os1.toString());

至此,base64分段编码才算大功告成。大文件上传核心代码才算大功告成。
其实代码改起来非常简单,但是不知道原因不知道原理的话,是无法无中生有的
对我本人来说原本只是想随便答一下,但没想到答的过程中发现自己有很多坑没有发现。答的过程中把自己不懂的地方没有发现的坑也完善了。不说碰到一个知识点就要追根究底,但实际开发中,每一个自己能亲身碰到的实际问题都是锻炼自己的绝佳机会,这种近距离触碰问题、解决问题的机会是难得的。虽然开发中还有很多其它问题也很重要,但你没有亲手碰到过,是无法共鸣的。所以自己在开发中碰到了问题,还是建议大概弄清原因。
弄清原理后,即使以后出现这个问题的其它"变种",也能找到原因并自己解决,但仅仅粘贴复制无法做到这一点。

3. AndroidManifest.xml 文件格式解析

图片左侧为解压开apk后,茄陵AndroidManifest.xml的二进制内容,右侧为各个chunk的解析。各颤渗戚个chunk和对应的二进制内容通过相同的颜色标记。

Android逆向三部曲之AndroidManifest.xml 文件格式 https://www.jianshu.com/p/f0f4856866e0

AndroidManifest二进制喊团文件格式分析: https://bbs.pediy.com/thread-194206.html

手把手教你解析AXML: https://blog.csdn.net/beyond702/article/details/51830108?spm=1001.2014.3001.5502

大小端模式: https://blog.csdn.net/q2519008/article/details/80961176

ResourceTypes.h 源文件: http://androidxref.com/9.0.0_r3/xref/frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h

4. android 如何获取保存的图片的地址 并存到数据库中

安卓中如何获取保存的图片uri 并保存到sqlite数据库中
有如下两种方法,仅供参考
方法一:Java代码

public void saveIcon(Bitmap icon) {
if (icon == null) {
return;
}
// 最终图标要保存到浏览器的内部数据库中,系统程序均保存为SQLite格式,Browser也不例外,因为图片是二进制的所以使用字节数组存储数据库的
// BLOB类型
final ByteArrayOutputStream os = new ByteArrayOutputStream();
// 将Bitmap压缩成PNG编码,质量为100%存储
icon.compress(Bitmap.CompressFormat.PNG, 100, os);
// 构造SQLite的Content对象,这里也可以使用
raw ContentValues values = new ContentValues();
// 写入数据库的
Browser.BookmarkColumns.TOUCH_ICON字段 values.put(Browser.BookmarkColumns.TOUCH_ICON, os.toByteArray());
DBUtil.update(....);
//调用更新或者插入到数据库的方法
}
}

方法二:如果数据表入口时一个content:URIJava代码

import android.provider.MediaStore.Images.Media;
import android.content.ContentValues;
import java.io.OutputStream;
// Save the name and description of an image in a ContentValues map.
ContentValues values = new ContentValues(3);
values.put(Media.DISPLAY_NAME, "road_trip_1");
values.put(Media.DESCRIPTION, "Day 1, trip to Los Angeles");
values.put(Media.MIME_TYPE, "image/jpeg");
// Add a new record without the bitmap, but with the values just set.
// insert() returns the URI of the new record.
Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);
// Now get a handle to the file for that record, and save the data into it.
// Here, sourceBitmap is a Bitmap object representing the file to save to the database.
try {
OutputStream outStream = getContentResolver().openOutputStream(uri);
sourceBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream);
outStream.close();
} catch (Exception e) {
Log.e(TAG, "exception while writing image", e);
}
原文请看http://www.bafenbaosoft.com/post/48.html

5. Android后台返回二进制图片字符串怎样转成图片显示在UI界面

你确定这个是图片的话,新建文件,保存,保存为相应的图片格式(最好加上扩展名,当然不加也行).
imageview读取
或者直接内存建立位图,写数据,显示.
你应该弄个16进制的看看,你这玩意啥也看不出来.

6. 如何编译android 可执行的二进制文件

Android由于其代码是放在dalvik虚拟机上的托管代码,所以能够很容易的将其反编译为我们可以识别的代码。 之前我写过一篇文章反编译Android的apk包到smali文件 然后再重新编译签名后打包实现篡改apk的功能。 最近又有一种新的方法来实现直接从Android apk包里的classes.dex文件,把dex码反编译到java的.class二进制码,然后从.class二进制码反编译到java源码想必就不用我来多说了吧。 首先我们需要的工具是dex2jar和jd-gui 其中第一个工具dex2jar是用来把classex.dex文件也就是dex二进制码转化为java的标准.class二进制码,然后jd-gui是把标准的.class二进制码再反编译为java源码。 首先从apk包里面提取出classes.dex 放到dex2jar目录下 然后在命令行下执行 dex2jar.bat classes.dex 这时执行成功后会生成一个classes.dex.dex2jar.jar文件 这个文件再用jd-gui打开,如下图 几乎跟我自己写的这个程序的源码一样 提供这个反编译方法主要用于大家学习与研究,有问题可以与我邮件讨论。 希望大家不要用这个方法从事恶意的活动,毕竟,别人辛辛苦苦写程序也不容易,用这个方法可以大致的学习到别人写的程序的逻辑,架构,希望对大家有所帮助。 Android 逆向apk程序的心得 本文主要介绍如何逆向一个Android的APK应用程序,本文提供的方法仅供研究学习之用。 本文需要用到的工具有 jdk 这个用于搭建java运行环境 AXMLPrinter2.jar 这个用于逆向.xml文件 baksmali.jar 这个用于逆向classex.dex文件 由于Android的.apk文件实际上就是一个zip文件 可以直接用winrar打开

阅读全文

与android二进制转图片相关的资料

热点内容
c编译器发布视频 浏览:799
python绝配 浏览:647
2017年安卓机皇是什么手机 浏览:830
程序员做手工制作 浏览:142
如何将服务器沉入水里 浏览:448
如何在屏幕上不显示常用app 浏览:508
python3课程 浏览:175
java8编程开发入门 浏览:952
如何找出文件夹中同一尺寸的图片 浏览:681
bg在服务器是什么意思 浏览:899
java资源下载 浏览:530
程序员学习的游戏软件 浏览:929
编程里的面向90度是哪个方向 浏览:84
pdf光标 浏览:581
java发布web项目 浏览:467
图形化拖拽编程框架 浏览:233
饥荒android版 浏览:569
福建编程机构万 浏览:322
烽火交换机命令 浏览:584
win10uefilinux 浏览:837