题外话:今天偶尔来逛逛,发现我真是懒到家了。居然有半年前的留言我都没有来看过,真对不起留言的同学,希望他的问题已经解决了。

这两三天一直被亚马逊S3上传文件的问题困扰着,直到昨天晚上终于搞定了,工作群里一片欢腾,从客户端到服务器数位工程师卡在这个问题上抓耳挠腮了好几天,终于解决了,这就是所谓“光明总出现在最黑暗的时刻”吧,嘿嘿,非常开心,程序员真是容易满足啊。

途中搜索了很多互联上的方案,最终在stackoverflow的一个帖子里找到有价值的参考。而我们国内的站点上关于这方面的信息非常少,所以我来写这个随笔,希望为大家增加一点参考吧。

------------------------------------------------------------正式开始的分割线----------------------------------------------------------------------------

一开始我们的代码是这样,上传一个测试的mp4,我们的需求是文件size不超过10M:

     File file = new File(Environment.getExternalStorageDirectory().getPath() + "/0.mp4");

     if(!file.exists())
return;
if(!file.isFile())
return;
try {
URL url = new URL("https://secv.s3.amazonaws.com/familytest10099/DFG092833/2016/01/27/10-38-07/0.mp4?AWSAccessKeyId=AKIAIGMMMZARXIK3ZDBA&Expires=1453948689&Signature=uJvHirNhOlCuB5z20NPkYc73qV8%3D");//从自家server签出的S3 地址
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("PUT");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(connection.getOutputStream());
FileInputStream fileInputStream = new FileInputStream(file);// 读取文件的数据。
byte[] bufer = new byte[1024];
int len = 0, i = 0;
while ((len = fileInputStream.read(bufer)) != -1) {
bufferedOutputStream.write(bufer, 0, len);
} bufferedOutputStream.flush();
bufferedOutputStream.close(); fileInputStream.close(); int responseCode = connection.getResponseCode();
Log.d("s3_ssltest","Service returned response code " + responseCode);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

运行结果抛出异常:

javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x54b61708: Failure in SSL library, usually a protocol error
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0x52732cfc:0x00000000)
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:448)
at com.android.okhttp.Connection.upgradeToTls(Connection.java:146)
at com.android.okhttp.Connection.connect(Connection.java:107)
at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:294)
at com.android.okhttp.internal.http.HttpEngine.sendSocketRequest(HttpEngine.java:255)
at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:206)
at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:345)
at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:296)
at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:503)
at com.android.okhttp.internal.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:136)
at com.spde.switchbox.manager.secu.SecuThread.a(Unknown Source)
at com.spde.switchbox.manager.secu.SecuThread.run(Unknown Source)
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x54b61708: Failure in SSL library, usually a protocol error
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0x52732cfc:0x00000000)
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:405)
... 11 more

出错原因是亚马逊s3 服务器端禁用了SSLv3,解决方法是自己extends一个SSLSocketFactory把SSLv3从默认的protocol中remove掉。重写的SSLSocketFactory见附件NoSSLv3Factory.java。

在创建connection之前先调用:

HttpsURLConnection.setDefaultSSLSocketFactory(new NoSSLv3Factory());

这样修改之后SSL的问题解决了,但又抛出一个异常

javax.net.ssl.SSLException: Write error: ssl=0x5ab6f5f8: I/O error during system call, Connection reset by peer
at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_write(Native Method)
at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLOutputStream.write(OpenSSLSocketImpl.java:693)
at java.io.ByteArrayOutputStream.writeTo(ByteArrayOutputStream.java:231)
at libcore.net.http.RetryableOutputStream.writeToSocket(RetryableOutputStream.java:70)
at libcore.net.http.HttpEngine.readResponse(HttpEngine.java:806)
at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:274)
at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:486)
at libcore.net.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:134)
at com.example.test.MainActivity$SSLTestRunnable.run(MainActivity.java:73)
at java.lang.Thread.run(Thread.java:856)

原因是http header中没有指定content-type,传输格式是stream

connection.setRequestProperty("Content-Type", "application/octet-stream");

这个异常也解决了。访问成功,但是返回403 Forbidden,网上搜到很多解释说是客户端的时区或者系统时间不对,因为S3上签出的url是有expire date的。但我们出问题的原因是因为服务器签出这个url的时候没有指定Content-type , 又是Content-type的问题,修改签出地址的代码如下:

public static String createUploadUrl(String bucket, String objKey, Date expire){
    String url = s3Client.generatePresignedUrl(new GeneratePresignedUrlRequest(bucket, objKey).
                withMethod(HttpMethod.PUT).
                withContentType("application/octet-stream").
               withExpiration(expire)
            ).toString();
    return url;
}

ok,这次终于收到回复200 ok了,视频也上传成功。

困扰三天的问题就这样解决了。总结一下:一个复杂迷茫各种想不通的问题,最终解决的时候往往修改不了几行代码。

额外的说明:我们出问题的平台android版本是4.4.3,openSSL lib的版本是1.0.1e。网上搜集到的信息,SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure 这种问题通常出现在Android 4.x上。

最后是 NoSSLv3Factory.java 的源码

 

最新文章

  1. Ext JS 如何动态加载JavaScript创建窗体
  2. Django自定义模板
  3. 数据库基础和JDBC
  4. PHP伪静态
  5. HDFS主要特性和体系结构
  6. malloc心得
  7. linux之flock函数锁文件
  8. C++ Primer 笔记(1)基础中的战斗机 输入输出 对输入不定数据处理
  9. Java动物声音模拟器
  10. MySQL查询in操作 查询结果按in集合顺序显示(转)
  11. java基础学习总结四(控制语句<顺序、选择、循环>、方法)
  12. 如何在同一系统里同时启动多个Tomcat
  13. springmvc的一些记录
  14. ng-class,与ng-click
  15. Laravel查询构造器的使用方法整理
  16. Linux 权限位详解
  17. zookeeper使用
  18. java web 测试
  19. 关于Unity中Mecanim动画的重定向与动画混合
  20. ie8兼容半透明效果css

热门文章

  1. java学习多线程之卖票示例
  2. Three ways to do WCF instance management
  3. CSS六大选择器(注释css表里不能加注释!!)
  4. jQuery选择器上下文
  5. ajax向后台传值
  6. JSP丶新闻发布会系统
  7. JavaScript高级程序设计31.pdf
  8. Android游戏与应用开发最佳学习路线图
  9. usaco 猜数游戏
  10. 武汉Uber优步司机奖励政策(2月1日~2月7日)