上一篇node.js 第三方模块如何安装(使用npm)及介绍 了formidable的安装,这一篇结合android写一个文件上传的例子。
先上服务端node.js 的代码,保存为upload.js
var http = require('http'); var fs = require('fs'); var formidable = require('formidable'); var firstPage = function(res){ res.writeHead(200, {'Content-Type': 'text/html'}); var html = fs.readFileSync(__dirname + '/public/form.html'); res.end(html); } var resultPage = function(res,data,files){ res.setHeader('Content-Type', 'text/html'); res.write('<p>thanks ' + data.name + '</p>'); res.write('<ul>'); console.log(data); console.log(files); if (Array.isArray(files.images)) { files.images.forEach(function(image){ var kb = image.size / 1024 | 0; res.write('<li>uploaded ' + image.name + ' ' + kb + 'kb</li>'); }); } else { var image = files.images; var kb = image.size / 1024 | 0; res.write('<li>uploaded ' + image.name + ' ' + kb + 'kb</li>'); } res.end('</ul>'); } var server = http.createServer(function(req, res) { if (req.method == 'GET'){ return firstPage(res); } var form = new formidable.IncomingForm; var data = {}; var files = {}; form.uploadDir = __dirname +'/file'; form.keepExtensions = true; function ondata(name, val, data){ if (Array.isArray(data[name])) {//数组 data[name].push(val); } else if (data[name]) {//同key data[name] = [data[name], val]; } else {//第一次 data[name] = val; } } form.on('field', function(name, val){ ondata(name, val, data); }); form.on('file', function(name, val){ ondata(name, val, files); }); form.on('end', function() { resultPage(res,data,files); }); form.parse(req); }); server.listen(8080); console.log('listening on http://localhost:8080');__dirname + '/public/form.html,在js当前目录下建立public文件夹,文件夹下建立form.html文件,文件内容如下
<html> <body> <form action="/" method="post" enctype="multipart/form-data"> <input type="text" name="name" placeholder="Name:" /> <input type="file" name="images" multiple="multiple" /> <input type="submit" value="Upload" /> </form> </body> </html>
代码比较简单,formidable部分可以上官网https://github.com/felixge/node-formidable看API。
运行以下看看
在浏览器中打开http://localhost:8080
选择文件,输入文件name
点击upload
后台截图
文件上传成功
再看android代码,布局文件main.xml如下
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/RelativeLayout1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:id="@+id/textViewInfo" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true"/> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_below="@+id/textViewInfo" android:layout_marginLeft="40dp" android:layout_marginTop="20dp" android:text="文件路径" android:textAppearance="?android:attr/textAppearanceMedium" /> <TextView android:id="@+id/textViewFile" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/textView1" android:layout_below="@+id/textView1"/> <TextView android:id="@+id/textView3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/textViewFile" android:layout_below="@+id/textViewFile" android:layout_marginTop="10dp" android:text="上传网址" android:textAppearance="?android:attr/textAppearanceMedium" /> <TextView android:id="@+id/textViewUrl" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/textView3" android:layout_below="@+id/textView3"/> <WebView android:id="@+id/webViewResult" android:layout_width="fill_parent" android:layout_height="100dp" android:layout_below="@+id/textViewUrl" android:layout_marginTop="30dp"/> <Button android:id="@+id/buttonJava" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginLeft="60dp" android:text="Java上传" /> <Button android:id="@+id/buttonApache" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBottom="@+id/buttonJava" android:layout_toRightOf="@+id/buttonJava" android:text="Apache上传" /> </RelativeLayout>主程序代码如下
package com.zhang.test08_11; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.ProtocolException; import java.net.URL; import java.nio.charset.Charset; import java.util.Random; import org.apache.http.HttpResponse; import org.apache.http.ParseException; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.FileBody; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.webkit.WebView; import android.widget.Button; import android.widget.TextView; public class Test08_11Activity extends Activity { private TextView textViewInfo; private TextView textViewFile; private TextView textViewUrl; private WebView webViewResult; private Button buttonJava; private Button buttonApache; private static final String UPLOAD_FILE = "/sdcard/test.jpg"; private static final String UPLOAD_URL = "http://192.168.9.194:8080/"; private static final int BUFFER_SIZE = 1024; //rfc1867协议 private static final String FIELD_SEP = ": "; private static final String CR_LF = "\r\n"; private static final String TWO_DASHES = "--"; private static final String BOUNDARY; /** * The pool of ASCII chars to be used for generating a multipart boundary. */ private final static char[] MULTIPART_CHARS = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); static { StringBuilder buffer = new StringBuilder(); Random rand = new Random(); int count = rand.nextInt(11) + 30; // a random size from 30 to 40 for (int i = 0; i < count; i++) { buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]); } BOUNDARY = buffer.toString(); Log.i("info", BOUNDARY); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); textViewInfo = (TextView)findViewById(R.id.textViewInfo); textViewFile = (TextView)findViewById(R.id.textViewFile); textViewUrl = (TextView)findViewById(R.id.textViewUrl); webViewResult = (WebView)findViewById(R.id.webViewResult); buttonJava = (Button)findViewById(R.id.buttonJava); buttonApache = (Button)findViewById(R.id.buttonApache); textViewFile.setText(UPLOAD_FILE); textViewUrl.setText(UPLOAD_URL); buttonJava.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { javaUpload(); } }); buttonApache.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { apacheUpload(); } }); } private void javaUpload(){ URL url = null; try { url = new URL(UPLOAD_URL); } catch (MalformedURLException e) { } HttpURLConnection urlConnection = null; try { urlConnection = (HttpURLConnection) url.openConnection(); } catch (IOException e) { textViewInfo.setText(e.getMessage()); return; } try { urlConnection.setRequestMethod("POST"); } catch (ProtocolException e) { } urlConnection.setDoOutput(true); urlConnection.setRequestProperty("Content-Type", "multipart/form-data; boundary="+ BOUNDARY + "; charset=UTF-8"); OutputStream out = null; try { out = new BufferedOutputStream(urlConnection.getOutputStream());//请求 } catch (IOException e) { urlConnection.disconnect(); textViewInfo.setText(e.getMessage()); return; } FileInputStream fStream = null; StringBuilder form = new StringBuilder(); form.append(TWO_DASHES + BOUNDARY + CR_LF); form.append("Content-Disposition" + FIELD_SEP + "form-data; name=\"name\"" + CR_LF); form.append("Content-Type" + FIELD_SEP + "text/plain; charset=UTF-8"+ CR_LF); form.append("Content-Transfer-Encoding" + FIELD_SEP + "8bit"+ CR_LF); form.append(CR_LF); form.append("testImage抓哇"); form.append(CR_LF); form.append(TWO_DASHES + BOUNDARY + CR_LF); form.append("Content-Disposition" + FIELD_SEP + "form-data; name=\"images\";filename=\"test抓哇.jpg\"" + CR_LF); form.append("Content-Type" + FIELD_SEP + "image/jpeg; charset=UTF-8"+ CR_LF); form.append("Content-Transfer-Encoding" + FIELD_SEP + "binary"+ CR_LF); form.append(CR_LF); try { out.write(form.toString().getBytes("UTF-8")); fStream = new FileInputStream(UPLOAD_FILE); byte[] buffer = new byte[BUFFER_SIZE]; int length = -1; while ((length = fStream.read(buffer)) != -1) { out.write(buffer,0,length); } out.write(CR_LF.getBytes("UTF_8")); out.write((TWO_DASHES + BOUNDARY + TWO_DASHES + CR_LF).getBytes("UTF-8")); out.flush(); } catch (IOException e) { try { out.close(); } catch (IOException e1) { } urlConnection.disconnect(); textViewInfo.setText(e.getMessage()); return; } finally { try { if (fStream != null) { fStream.close(); } } catch (IOException e) { } } getResponseJava(urlConnection); } private void apacheUpload(){ HttpPost request = new HttpPost(UPLOAD_URL); FileBody bin = new FileBody(new File(UPLOAD_FILE),"test阿帕奇.jpg","image/jpeg",HTTP.UTF_8); StringBody comment = null; try { comment = new StringBody("testImage阿帕奇", Charset.forName(HTTP.UTF_8)); } catch (UnsupportedEncodingException e) { } MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName(HTTP.UTF_8)); entity.addPart("images", bin); entity.addPart("name", comment); request.setEntity(entity); getResponseApache(request); } private void getResponseJava(HttpURLConnection urlConnection) { InputStream in = null; try { in = new BufferedInputStream(urlConnection.getInputStream());//响应 } catch (IOException e) { urlConnection.disconnect(); textViewInfo.setText(e.getMessage()); return; } BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(in,"UTF-8")); } catch (UnsupportedEncodingException e1) { } StringBuilder result = new StringBuilder(); String tmp = null; try { while((tmp = reader.readLine()) != null){ result.append(tmp); } } catch (IOException e) { textViewInfo.setText(e.getMessage()); return; } finally { try { reader.close(); urlConnection.disconnect(); } catch (IOException e) { } } webViewResult.loadDataWithBaseURL(null, result.toString(), "text/html", "UTF-8", null); } private void getResponseApache(HttpUriRequest request) { HttpClient client = new DefaultHttpClient(); HttpResponse response = null; try { response = client.execute(request); } catch (ClientProtocolException e) { textViewInfo.setText(e.getMessage()); } catch (IOException e) { textViewInfo.setText(e.getMessage()); } if (response == null) { return; } String result = null; if (response.getStatusLine().getStatusCode() == 200) { try { result = EntityUtils.toString(response.getEntity(),HTTP.UTF_8); } catch (ParseException e) { result = e.getMessage(); } catch (IOException e) { result = e.getMessage(); } } else { result = "error response" + response.getStatusLine().toString(); } //Log.i("info", result); //不乱码 //webViewResult.loadData(result, "text/html", "UTF-8"); //乱码 webViewResult.loadDataWithBaseURL(null, result, "text/html", "UTF-8", null);//不乱码 } }别忘了加权限
<uses-permission android:name="android.permission.INTERNET"/>
后台输出
文件上传成功
点击Apache上传
后台输出
文件保存成功
运行一切OK。
解释一下代码:
private static final String UPLOAD_FILE = "/sdcard/test.jpg"; private static final String UPLOAD_URL = "http://192.168.9.194:8080/";
代码其它部分主要是要了解http文件上传的协议 RFC1867,http://www.ietf.org/rfc/rfc1867.txt IETF官方介绍,它提供的例子不错
6. Examples Suppose the server supplies the following HTML: <FORM ACTION="http://server.dom/cgi/handle" ENCTYPE="multipart/form-data" METHOD=POST> What is your name? <INPUT TYPE=TEXT NAME=submitter> What files are you sending? <INPUT TYPE=FILE NAME=pics> </FORM> and the user types "Joe Blow" in the name field, and selects a text file "file1.txt" for the answer to 'What files are you sending?' The client might send back the following data: Content-type: multipart/form-data, boundary=AaB03x --AaB03x content-disposition: form-data; name="field1" Joe Blow --AaB03x content-disposition: form-data; name="pics"; filename="file1.txt" Content-Type: text/plain ... contents of file1.txt ... --AaB03x-- If the user also indicated an image file "file2.gif" for the answer to 'What files are you sending?', the client might client might send back the following data: Content-type: multipart/form-data, boundary=AaB03x --AaB03x content-disposition: form-data; name="field1" Joe Blow --AaB03x content-disposition: form-data; name="pics" Content-type: multipart/mixed, boundary=BbC04y --BbC04y Content-disposition: attachment; filename="file1.txt"