在stackoverflow上有个关于Android大文件上传的讨论。尝试着用Apache HttpComponents来解决。一番折腾,几多挫折,相当有趣。
环境准备
习惯了在Eclipse上开发,于是下载官方插件。早早升级到Eclipse4.3,当然是下载Google Update Site for Eclipse 4.3。这个插件包括GAE和GWT在内,需要挑选出Android的才好。
安装好的插件没有Android的SDK的,只有个SDK Manager,用这个再去下载及安装。在我折腾时4.3刚刚发布,但是没有Intel x86 Atom模拟器,只好4.2.2和4.3都装了。
个人的选择是这样的:
- SDK Platform是必须的
- Sample和Source也最好有,本地查资料快些。
- 由于是在windwos上开发的,所以那个Intel x86 Atom虚拟机是需要的。(怕是只需要这个吧。
最后Extras里面的选择,Windows的话基本上必选那个Intel x86 Emulator Accelerator(HAXM),不然模拟器会慢到“飘起”。即使装上也没快不到哪里去,我那2010年的笔记本上虽然也能跑起来,但风扇也会不停的尖叫着...据我“耳测”,大抵接近《魔兽世界》或《星际争霸II》的水平。我了去的!
伴随着风扇的尖叫,折腾好那个简陋得不能再简陋的界面后,才发现我这个代码其实绝大部分无须Android的。(等我先去耳鸣一会儿...
主要思路
基本上是将大文件拆成小块,读取,上传。当所有文件块上传完成后,合并。
- 读取大文件。考虑到I/O竞争只用单线程;考虑到内存采取分批读取。
ByteBuffer bb = ByteBuffer.allocate(partSize); int bytesRead = fc.read(bb); if (bytesRead == -1) { break; } byte[] bytes = bb.array(); parts.put(new Part(createFileName(fileName, i), bytes));
- 将读取的文件块上传。通常多个线程会好些,但是太多又不行,默认用5线程上传。
Part part = parts.take(); if (part == Part.NULL) { parts.add(Part.NULL);// notify others to stop. break; } else { uploader.upload(part); }
- 读取和上传用BlockingQueue通信,最通用的生产/消费者模式。
- 服务器在接收到全部文件块后,合并。
- 还需考虑处理传送失败时,重新尝试上传部分。
- 由于Android的问题(参考下面Invocation Exception),在Android上使用时需要用jarjar这个工具先处理下。
JDK5追加的java.util.concurrent功能,一直都是“纸上谈兵”,从来没真正使用过。这次好好折腾了下...
代码(优化中)上传到GitHub,如有兴趣或有需要请移步。
挫折坎坷
Invocation Exception
起手遇到这个问题,在Eclipse本地跑程序还没问题,装进Andorid模拟器后,启动就报错。查下来HttpComponents各种方法找不到。可明明jar包都扔进去的说。网上翻了好久,才搞清楚Android里面有个旧版本的HttpComponents在作怪!就让人有些火大的说!
您弄个老版本扔在那里,能不能想着升级下呢?听说还是beta版!
然后就是Class加载顺序,自然该是source,自带jar包,最后才是系统默认的优先顺序才对。这是多么基本的规矩啊?!
搜索时还看到好像是Android开发组(Dalvik team)的某位跑出来,推荐用HttpURLConnection代替HttpComponents。啥?!大哥你可以再搞笑些吗?虽然HttpComponents 4.x的评价不那么好,但你分别写个文件上传代码看看,差多少?差“老鼻子”了!
最后,既然不推荐,你把老版本去掉好吗?这才叫占茅坑...
解决办法:用jarjar这个工具把最新版的HttpComponents都换下包名,程序中引用指向这个新包。下面是jarjar的rule,只替换org.apache.http,放过其他的比如org.apache.commons等。因为Android里面还藏了大票apache commons的东东!(在我花了九牛二虎之力,企图或是重新打包commons,或是提供‘伪’实现,一通折腾之后才发现!
rule org.apache.http.** repack.org.apache.http.@1
Connection refused
解决掉包冲突后可以运行了。首当其冲的就是这个“拒绝连接”的错误。Android的AndroidManifest.xml里permission也加过了。
<uses-permission android:name="android.permission.INTERNET" />
但还是失败。最终找到了问题所在——没把模拟器当成独立的环境,习惯上地址直接写http://localhost/xxx!模拟器里的localhost是模拟器自己,而不是宿主机器。又因为是在Windows上开发的,所以最方便就是IP地址——localhost改成192.168.x.x的局域网地址即可。
Request not MultipartHttpRequest
终于能和服务器通信了,但服务器端不停报错ClassCastException,说Request不是MultipartHttpServletRequest。
解决办法:这个是我自己的问题。临时找来的测试环境,Spring的multipartResolver就根本没配置。
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" p:defaultEncoding="utf-8" />
文件拷贝
这个就不细说了,说多了都是丢人!单单个from和to把自己绕晕了好久...
说结果吧。假如有两个FileChannel,分别是src和dest,需要将src的内容追加到dest的后面,该如何处理?有两种方法:
-
dest.transferFrom(src, 其他参数);
-
src.transferTo(其他参数, dest);
效果是一样的,细微的区别在于——作为参数的FileChannel的内部position会被改变!如果你对position有需求,那么就要当心了!(我在这里被绕晕的原因...应该是风扇还在一旁尖叫着...风扇应该怪模拟器...所以你看,最终都是google不好!灭哈哈!
Connection Timeout
这个要怪Apache了。4.2后多了释放连接的方法,如果不显式调用,就不释放连接。导致其他线程在拿连接时timeout。4.2之后的写法如下:
try { HttpResponse response = client.execute(post); // do something. } catch (Exception e) { post.abort(); throw new RuntimeException(e); } finally { post.releaseConnection(); }
大叔的抱怨
多年前(2009)曾经摸过Android,当时只是觉得它的源代码比较粗糙。4年过去了,现在看来,还是...糙!
相关推荐
java实现大文件上传并有进度条及其代码解析
完整的java大文件分块上传支持断点续传,包含jar包,可以直接导入eclipse使用。
java大文件上传至ftp服务器带进度条显示的,进度条的显示和ftp的上传速度同步,解决了进度条显示和ftp不同步的问题
android上传大文件亲测可用,上传200M个文件,不到3分钟
android客户端向java服务器端上传文件(完整版),内嵌了一个android文件浏览器 绝对完美欢迎下载
java实现的大文件上传,支持断点续传 支持分片分片上传 采用springboot框架是实现
android客户端向java服务器端上传文件示例。android客户端向java服务器端上传文件示例。
此文件包含生成android签名文件keystore及验证的源码还包含生成的keystore文件。放到项目中即可使用
android--java写日志文件 android--java写日志文件 android--java写日志文件android--java写日志文件
java使用swf+ext实现文件批量上传,直接导入Eclipse就可以使用。
可自主限制上传文件的文件类型 showProgress "" "" "" 方法的第二个参数为限制上传文件类型 多文件时用 ‘|’ 进行分割 如: zip| rar| jif
基于java语言实现文件的本地上传,oss普通上传,和大文件的分段上传,请手写的,可以使用,可以上传图片,视频压缩包等多种格式文件
java Android乒乓球java Android乒乓球java Android乒乓球java Android乒乓球java Android乒乓球java Android乒乓球java Android乒乓球java Android乒乓球java Android乒乓球java Android乒乓球java Android乒乓球...
Android的apk文件反编译后,甚多的资源和java文件无法对应,java文件中引用的资源id变成十进制数字。本文将反编译apk的java文件和资源文件重新关联引用对应,使得java文件能准确无误地重新引用资源文件。 (1)将反...
这个类可以实现,java语言下通过对上传文件进行MD5加密,然后将加密值与原来上传的文件加密值进行对比,防止上传重复文件。
android 多渠道 sourceset main文件夹下和其他渠道下的java文件重复 exclude方式解决
java Android切水果java Android切水果java Android切水果java Android切水果java Android切水果java Android切水果java Android切水果java Android切水果java Android切水果java Android切水果java Android切水果...
课程设计大作业基于java的Android Studio实现井字棋小游戏源码课程设计大作业基于java的Android Studio实现井字棋小游戏源码课程设计大作业基于java的Android Studio实现井字棋小游戏源码课程设计大作业基于java的...
在java代码中实现文件的上传和下载,通过页面的file文件上传到java代码段,获取文件的大小和名字
java Android15款java Android15款java Android15款java Android15款java Android15款java Android15款java Android15款java Android15款java Android15款java Android15款java Android15款java Android15款java ...