昨天,(在我的上一篇博客中)写了通过浏览器上传图片到服务器(php),今天将这个功能付诸实践.(还完善了服务端的代码)
不试不知道,原来通过android 向服务端发送图片还真是挺麻烦的一件事.
上传代码:
/* 上传文件至Server的方法 */ private void uploadFile() { String end = " "; String twoHyphens = "--"; String boundary = "*****"; try { URL url = new URL(postUrl); HttpURLConnection con = (HttpURLConnection) url.openConnection(); /* Output to the connection. Default is false, set to true because post method must write something to the connection */ con.setDoOutput(true); /* Read from the connection. Default is true.*/ con.setDoInput(true); /* Post cannot use caches */ con.setUseCaches(false); /* Set the post method. Default is GET*/ con.setRequestMethod("POST"); /* 设置请求属性 */ con.setRequestProperty("Connection", "Keep-Alive"); con.setRequestProperty("Charset", "UTF-8"); con.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); /*设置StrictMode 否则HTTPURLConnection连接失败,因为这是在主进程中进行网络连接*/ StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().detectNetwork().penaltyLog().build()); /* 设置DataOutputStream,getOutputStream中默认调用connect()*/ DataOutputStream ds = new DataOutputStream(con.getOutputStream()); //output to the connection ds.writeBytes(twoHyphens + boundary + end); ds.writeBytes("Content-Disposition: form-data; " + "name="file";filename="" + fileName + """ + end); ds.writeBytes(end); /* 取得文件的FileInputStream */ FileInputStream fStream = new FileInputStream(uploadFile); /* 设置每次写入8192bytes */ int bufferSize = 8192; byte[] buffer = new byte[bufferSize]; //8k int length = -1; /* 从文件读取数据至缓冲区 */ while ((length = fStream.read(buffer)) != -1) { /* 将资料写入DataOutputStream中 */ ds.write(buffer, 0, length); } ds.writeBytes(end); ds.writeBytes(twoHyphens + boundary + twoHyphens + end); /* 关闭流,写入的东西自动生成Http正文*/ fStream.close(); /* 关闭DataOutputStream */ ds.close(); /* 从返回的输入流读取响应信息 */ InputStream is = con.getInputStream(); //input from the connection 正式建立HTTP连接 int ch; StringBuffer b = new StringBuffer(); while ((ch = is.read()) != -1) { b.append((char) ch); } /* 显示网页响应内容 */ // Toast.makeText(MainActivity.this, b.toString().trim(), Toast.LENGTH_SHORT).show();//Post成功 System.out.println(b.toString()); } catch (Exception e) { /* 显示异常信息 */ // Toast.makeText(MainActivity.this, "Fail:" + e, Toast.LENGTH_SHORT).show();//Post失败 System.out.println(e); } }
上篇博文有说上传图片必须使用POST方法,这里当然使用的就是post方法.这里上传跟普通上传写法没啥变化.
不过我还是不习惯使用这个方法(我一直使用volley框架, 有兴趣可以百度一下.一个官方推出的很好用的框架,要不是需要用到传图我还不会研究这个写法)
使用原生POST方法的基本步骤:
1.使用 HttpURLConnection 建立连接
2.设置请求属性
3.根据请求格式组装参数(很麻烦 , 可以通过浏览器发送POST请求,取出调试信息对比)
4.建立输入流,并写入数据(FileInputStream 和下面循环写入哪里)
5.获取返回流,并处理返回参数
6.关闭所有流
该部分代码几乎完全借鉴大神的写法.里面注释也很多我也不多说了.
值得一提的是在4.0(android 版本)以后就已经不支持在主线程中执行上传,下载等耗时操作了.
这也很合理,假如你在上传文件的时候,突然你的网络不好,你就会一直在上传状态.体验很不好.
所以调用上面的方法需要新建一个线程:
new Thread(new Runnable() { @Override public void run() { uploadFile(); } }).start();
这个调用方法是在上传方法与该方法同文件时可以这样写,如果上传方法在独立的class文件中需要先实例化然后通过
实例化对象名.uploadFile();
的方法调用.
服务器端
上篇博文里面只是简单的实现了,接收并转存图片.这里对该服务端进行完善:
<?php /** * 接收上传的图片 * * ------------ * 200 ok * 401 Error method * 500 Internal error * ------------ */ // ini_set("display_errors", "On"); // error_reporting(E_ALL | E_STRICT); header('Access-Control-Allow-Origin: *'); // 解决前段javascript跨域请求 $fileInfo = $_FILES['file']; $maxSize=2097152;//允许的最大值 $allowExt=array('jpeg','jpg','png','gif','wbmp'); $flag=true;//检测是否为真实图片类型 if($fileInfo['error']==0){ //判断上传文件的大小 if($fileInfo['size']>$maxSize){ $data = '上传文件过大'; return Response::show(201,'error1', $data); exit(); } //$ext=strtolower(end(explode('.',$fileInfo['name'])));获取后缀 $ext=pathinfo($fileInfo['name'],PATHINFO_EXTENSION); if(!in_array($ext,$allowExt)){ $data = '非法文件类型'; return Response::show(202 , 'error2' , $data); exit(); } //判断文件是否是通过HTTP POST方式上传来的 if(!is_uploaded_file($fileInfo['tmp_name'])){ $data = '文件不是通过HTTP POST方式上传来的'; return Response::show(203 , 'error3' , $data); exit(); } //检测是否为真实的图片类型 if($flag){ if(!getimagesize($fileInfo['tmp_name'])){ $data = '不是真正图片类型'; return Response::show(204 , 'error4' , $data); exit(); } } //创建与id对应的文件夹 $id = $_GET['id']; if(!file_exists($id)){ mkdir('../files/'.$id); //chmod($id , 0777); } if(@move_uploaded_file($fileInfo['tmp_name'],'../files/'.$id.'/'.$fileInfo['name'])){ $data = '文件上传成功'; return Response::show(200 , 'ok' , $data); }else{ $data = '文件上传失败'; return Response::show(404 , 'error5' , $data); } }else{ switch($fileinfo['error']){ case 1: $data = '上传文件超过了PHP配置文件中upload_max_filesize选项的值'; return Response::show(401 , 'error5' , $data); break; case 2: $data = '超过了表单MAX_FILE_SIZE限制的大小'; return Response::show(402 , 'error5' , $data); break; case 3: $data = '文件部分被上传'; return Response::show(403 , 'error5' , $data); break; case 4: $data = '没有选择上传文件'; return Response::show(405 , 'error5' , $data); break; case 6: $data = '没有找到临时目录'; return Response::show(405 , 'error5' , $data); break; case 7: case 8: $data = '系统错误'; return Response::show(405 , 'error5' , $data); break; } }
这里对图片进行了各种检测.比昨天的更可靠了是吧.还统一了返回的json格式方便客户端进行解析.(还有就是,上面的服务端的代码也是借鉴了慕课大神的写法,略作修改)