首先我们需要使用HTTP协议发送数据,我们就要知道HTTP发送上传文件到服务器的时候需要哪些头字段已经相关的配置,请看下图
这是使用浏览器模拟上传文件到服务器时候所发送的请求,我们可以看到它包含了请求头字段和实体部分,但是多了一个---------------------------7da2137580612,它实际上是一条分隔线,用于分隔实体数据的,他在使用分隔实体数据的时候会在前面包含多两个"-"而在结束的时候会在除了在前面都出两个减号"-"之外,还会在末尾都出两个减号"-"表示结束。初次之外,在Content-Type这个头字段当中,大家可以看到他的头字段是"multipart/form-data",因为只有当Content-Type头字段设置为该值的时候,服务器才会接受文件并且保存下来。然后他的组成依次是:请求头字段+两个回车换行(\r\n),然后是分隔线(记得加上两个减号)之后是一个回车换行(\r\n),之后两个回车换行,到数据值,之后是分隔线,这样依次下去
实现该功能的大概思路:因为是有关于网络的操作,不能直接在UI线程中执行,所以开一条线程去完成上传功能(关于断点上传以后再与大家分享)。然后线程一定需要注意的时候需要添加一个属于该线程的Looper,不然程序会崩溃掉。然后在MVC中的C层去控制下载。下载代码过程主要是难在如何组拼请求数据,最难的水Content-Length这个头字段的值。但是可以通过Content-Length=文本+文件,就可以算出他的值。
下面是具体代码实现:
MainActivity.java,他的主要功能是提供一个界面,然后有一个按钮和三个输入框架,点击按钮之后就可以上传文件,当文件位于SD卡根目录而且存在的时候则上传,否则只是上传title和length。他开了一条子线程去完成上传的任务。该类主要有四个方法,分别是
1,public static boolean save(String title, String length),对于需要上传文件不存在但是可以上传title和length
2,public static boolean save(String title, String length, File uploadFile),用于上传的文件存在的时候则是调用该方法上传
3,private static boolean sendHttpClientPOSTRequest(String path, Map<String, String> params, String encoding)用于发送GET或者POST请求,被第一个save方法调用
4,public static boolean post(String path, Map<String, String> params, FormFile[] files)用于上传文件,被第二个save方法调用。
public class MainActivity extends Activity { /* 上传文件标题 */ private EditText titleText; /* 上传的长度 */ private EditText lengthText; /* 上传的文件名称 */ private EditText nameText; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); titleText = (EditText) this.findViewById(R.id.title); lengthText = (EditText) this.findViewById(R.id.timelength); nameText = (EditText) this.findViewById(R.id.filename); } /** * 点击保存按钮之后用户将会上传文件 * @param v 按钮 */ public void save(View v) { String filename = nameText.getText().toString(); String title = titleText.getText().toString(); String length = lengthText.getText().toString(); UploadThread uploadThread = new UploadThread(filename, title, length, this); uploadThread.start(); } } @SuppressLint("ShowToast") class UploadThread extends Thread { String filename; String title; String length; Context context; public UploadThread (String filename , String title , String length , Context context) { super(); this.filename = filename; this.title = title; this.length = length; this.context = context; } @Override public void run() { /* * 检测SD卡的情况,MEDIA_MOUNTED表明sd对象是存在并具有读/写权限 * MEDIA_MOUNTED_READ_ONLY表明SD卡只读 */ Looper.prepare(); if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) || Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY)) { File uploadFile = new File(Environment.getExternalStorageDirectory(), filename);// 位于根目录下面,文件名是filename if (uploadFile.exists()) { boolean result = NewsService.save(title, length, uploadFile);// 假如文件存在则上传,包括title,length已经file if (result) { Toast.makeText(context, R.string.success, 1).show(); } else { Toast.makeText(context, R.string.error, 1).show(); } } else { Toast.makeText(context, R.string.filenoexsit, 1).show(); } } else { boolean result = NewsService.save(title, length);//假如文件不存在,则只是上传title和length if (result) { Toast.makeText(context, R.string.success, 1).show(); } else { Toast.makeText(context, R.string.error, 1).show(); } } Looper.loop(); } }Service类
public class NewsService { /** * 保存数据 这是当没有上传成功的时候调用 * @param title 标题 * @param length 时长 * @return */ public static boolean save(String title, String length) { String path = "http://192.168.0.168:8080/web/ManageServlet"; Map<String, String> params = new HashMap<String, String>(); params.put("title", title); params.put("timelength", length); try { return sendHttpClientPOSTRequest(path, params, "UTF-8"); } catch (Exception e) { e.printStackTrace(); } return false; } /** * 通过HttpClient发送Post请求 也可以直接使用GET或者POST发送请求 * @param path 请求路径 * @param params 请求参数 * @param encoding 编码 * @return 请求是否成功 */ private static boolean sendHttpClientPOSTRequest(String path, Map<String, String> params, String encoding) throws Exception{ List<NameValuePair> pairs = new ArrayList<NameValuePair>();//存放请求参数 if(params!=null && !params.isEmpty()){ for(Map.Entry<String, String> entry : params.entrySet()){ pairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } } UrlEncodedFormEntity entity = new UrlEncodedFormEntity(pairs, encoding); HttpPost httpPost = new HttpPost(path); httpPost.setEntity(entity); DefaultHttpClient client = new DefaultHttpClient(); HttpResponse response = client.execute(httpPost); if(response.getStatusLine().getStatusCode() == 200){ return true; } return false; } /** * 上传文件 * @param title 文件名称 * @param length 长度 * @param uploadFile 文件 * @return */ public static boolean save(String title, String length, File uploadFile) { String path = "http://192.168.1.125:8080/web/ManagerServlet"; Map<String, String> params = new HashMap<String, String>(); params.put("title", title); params.put("timelength", length); FormFile formFile = new FormFile(uploadFile, "videofile", "image/gif");//指定上传文件的类型 try { return post(path, params, new FormFile[]{ formFile}); } catch (Exception e) { e.printStackTrace(); } return false; } /** * 真正实现上传文件的类 * @param path 上传路径,即是服务器的路径,可以是域名 * @param params 请求参数 key为参数名,value为参数值 * @param file[] 上传文件,可以是多个 */ <pre name="code" class="java"> public static boolean post(String path, Map<String, String> params, FormFile[] files) { final String BOUNDARY = "---------------------------7da2137580612"; // 数据分隔线 final String endline = "--" + BOUNDARY + "--\r\n";// 数据结束标志,后面包含一个回车换行符号 int fileDataLength = 0; for (FormFile uploadFile : files) {// 得到文件file类型数据的总长度,加入当同事上传多个文件的时候,需要使用到迭代,每一次迭代算出的是一个文件的Http长度 StringBuilder fileExplain = new StringBuilder(); fileExplain.append("--"); fileExplain.append(BOUNDARY); fileExplain.append("\r\n"); // Content-Disposition这个头字段是用于设置当用户不能取得正确的文件名称的时候的默认名称,有时候也会死从服务器返回给用户,这时候用户从服务器下载数据但是取得文件名称失败的时候也会使用这个作为文件的默认名称 fileExplain.append("Content-Disposition: form-data;name=\"" + uploadFile.getParameterName() + "\";filename=\"" + uploadFile.getFilname() + "\"\r\n"); fileExplain.append("Content-Type: " + uploadFile.getContentType() + "\r\n\r\n"); fileDataLength += fileExplain.length(); if (uploadFile.getInStream() != null) { fileDataLength += uploadFile.getFile().length(); } else { fileDataLength += uploadFile.getData().length; } fileDataLength += "\r\n".length(); } StringBuilder textEntity = new StringBuilder(); for (Map.Entry<String, String> entry : params.entrySet()) {// 构造文本类型参数的实体数据 textEntity.append("--"); textEntity.append(BOUNDARY); textEntity.append("\r\n"); textEntity.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"\r\n\r\n"); textEntity.append(entry.getValue()); textEntity.append("\r\n"); } // 计算传输给服务器的实体数据总长度 int dataLength = textEntity.toString().getBytes().length + fileDataLength + endline.getBytes().length; Socket socket = null; OutputStream outStream = null; BufferedReader reader = null; try { URL url = new URL(path); int port = url.getPort() == -1 ? 80 : url.getPort(); socket = new Socket(InetAddress.getByName(url.getHost()), port); outStream = socket.getOutputStream(); // 下面完成HTTP请求头的发送 String requestmethod = "POST " + url.getPath() + " HTTP/1.1\r\n"; outStream.write(requestmethod.getBytes()); String accept = "Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*\r\n"; outStream.write(accept.getBytes()); String language = "Accept-Language: zh-CN\r\n"; outStream.write(language.getBytes()); String contenttype = "Content-Type: multipart/form-data; boundary=" + BOUNDARY + "\r\n"; outStream.write(contenttype.getBytes()); String contentlength = "Content-Length: " + dataLength + "\r\n"; outStream.write(contentlength.getBytes()); String alive = "Connection: Keep-Alive\r\n"; outStream.write(alive.getBytes()); String host = "Host: " + url.getHost() + ":" + port + "\r\n"; outStream.write(host.getBytes()); // 写完HTTP请求头后根据HTTP协议再写一个回车换行 outStream.write("\r\n".getBytes()); // 把所有文本类型的实体数据发送出来 outStream.write(textEntity.toString().getBytes()); // 把所有文件类型的实体数据发送出来 for (FormFile uploadFile : files) { StringBuilder fileEntity = new StringBuilder(); fileEntity.append("--"); fileEntity.append(BOUNDARY); fileEntity.append("\r\n"); fileEntity.append("Content-Disposition: form-data;name=\"" + uploadFile.getParameterName() + "\";filename=\"" + uploadFile.getFilname() + "\"\r\n"); fileEntity.append("Content-Type: " + uploadFile.getContentType() + "\r\n\r\n"); outStream.write(fileEntity.toString().getBytes()); if (uploadFile.getInStream() != null) { byte[] buffer = new byte[1024]; int len = 0; while ((len = uploadFile.getInStream().read(buffer, 0, 1024)) != -1) { outStream.write(buffer, 0, len); } uploadFile.getInStream().close(); } else { outStream.write(uploadFile.getData(), 0, uploadFile.getData().length); } outStream.write("\r\n".getBytes()); } // 下面发送数据结束标志,表示数据已经结束 outStream.write(endline.getBytes()); reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); if (reader.readLine().indexOf("200") == -1) {// 读取web服务器返回的数据,判断请求码是否为200,如果不是200,代表请求失败 return false; } outStream.flush(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { try { outStream.close(); } catch (IOException e) { e.printStackTrace(); } try { reader.close(); } catch (IOException e) { e.printStackTrace(); } try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } return true; }
原文地址:http://blog.csdn.net/liweijie_chengxuyuan/article/details/45048961