上一篇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"/>
先看看运行效果
点击java上传
后台输出
文件上传成功
点击Apache上传
后台输出
文件保存成功
运行一切OK。
解释一下代码:
private static final String UPLOAD_FILE = "/sdcard/test.jpg";
private static final String UPLOAD_URL = "http://192.168.9.194:8080/";
在sd卡根目录上放上test.jpg文件, node.js+android http请求响应 讲过,不要使用localhost。
代码其它部分主要是要了解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"
如果看不太懂的话,可以在搜索引擎里找 rfc1867实现。