先上代码,上传文件的封装工具类如下:
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.util.UUID;
/**
* Created by wishes on 2018/7/16.
*/
public class UploadUtil {
public static final String TAG = UploadUtil.class.getName();
private static final String CHARSET = "utf-8"; // 设置编码
private static final int TIME_OUT = 8*1000;
public static String result = null;
/**
* Android上传文件到服务端
*
* @param file 需要上传的文件
* @param RequestURL 请求的rul
* @return 返回响应的内容
*/
public static String uploadFile(final File file, final String RequestURL) {
// Thread thread = new Thread(new Runnable() {
// @Override
// public void run() {
String result = null;
String BOUNDARY = UUID.randomUUID().toString(); // 边界标识 随机生成
String PREFIX = "--", LINE_END = "\r\n";
String CONTENT_TYPE = "multipart/form-data"; // 内容类型
try {
URL url = new URL(RequestURL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(TIME_OUT);
conn.setConnectTimeout(TIME_OUT);
conn.setDoInput(true); // 允许输入流
conn.setDoOutput(true); // 允许输出流
conn.setUseCaches(false); // 不允许使用缓存
conn.setRequestMethod("POST"); // 请求方式
conn.setRequestProperty("Charset", CHARSET); // 设置编码
conn.setRequestProperty("connection", "keep-alive");
conn.setRequestProperty("Content-Type", CONTENT_TYPE + ";boundary=" + BOUNDARY);
if (file != null) {
/**
* 当文件不为空,把文件包装并且上传
*/
DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
StringBuffer sb = new StringBuffer();
sb.append(PREFIX);
sb.append(BOUNDARY);
sb.append(LINE_END);
/**
* 这里重点注意: name里面的值为服务端需要key 只有这个key 才可以得到对应的文件
* filename是文件的名字,包含后缀名的 比如:abc.png
*/
sb.append("Content-Disposition: form-data; name=\"upload\"; filename=\""
+ file.getName() + "\"" + LINE_END);
sb.append("Content-Type: application/octet-stream; charset=" + CHARSET + LINE_END);
sb.append(LINE_END);
dos.write(sb.toString().getBytes());
InputStream is = new FileInputStream(file);
byte[] bytes = new byte[1024];
int len = 0;
while ((len = is.read(bytes)) != -1) {
dos.write(bytes, 0, len);
}
is.close();
dos.write(LINE_END.getBytes());
byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINE_END).getBytes();
dos.write(end_data);
dos.flush();
/**
* 获取响应码 200=成功 当响应成功,获取响应的流
*/
int res = conn.getResponseCode();
LOG.i(TAG, "response code:" + res);
if(res==200)
{
LOG.i(TAG, "request success");
InputStream input = conn.getInputStream();
StringBuffer sb1 = new StringBuffer();
int ss;
while ((ss = input.read()) != -1) {
sb1.append((char) ss);
}
result = sb1.toString();
UploadUtil.result = new String(result.getBytes("iso-8859-1"),"utf-8");
LOG.i(TAG, "result : " + UploadUtil.result);
input.close();
}
else{
LOG.e(TAG, "request error");
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// }
// });
// thread.start();
LOG.i(TAG, "---"+UploadUtil.result+"---");
return UploadUtil.result;
}
/**
* 通过拼接的方式构造请求内容,实现参数传输以及文件传输
*
* @param url Service net address
* @param params text content
* @param files pictures
* @return String result of Service response
* @throws IOException
*/
public static String post(String url, Map<String, String> params, Map<String, File> files)
throws IOException {
String BOUNDARY = java.util.UUID.randomUUID().toString();
String PREFIX = "--", LINEND = "\r\n";
String MULTIPART_FROM_DATA = "multipart/form-data";
String CHARSET = "UTF-8";
URL uri = new URL(url);
HttpURLConnection conn = (HttpURLConnection) uri.openConnection();
conn.setReadTimeout(10 * 1000); // 缓存的最长时间
conn.setDoInput(true);// 允许输入
conn.setDoOutput(true);// 允许输出
conn.setUseCaches(false); // 不允许使用缓存
conn.setRequestMethod("POST");
conn.setRequestProperty("connection", "keep-alive");
conn.setRequestProperty("Charsert", "UTF-8");
conn.setRequestProperty("Content-Type", MULTIPART_FROM_DATA + ";boundary=" + BOUNDARY);
// 首先组拼文本类型的参数
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
sb.append(PREFIX);
sb.append(BOUNDARY);
sb.append(LINEND);
sb.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + LINEND);
sb.append("Content-Type: text/plain; charset=" + CHARSET + LINEND);
sb.append("Content-Transfer-Encoding: 8bit" + LINEND);
sb.append(LINEND);
sb.append(entry.getValue());
sb.append(LINEND);
}
DataOutputStream outStream = new DataOutputStream(conn.getOutputStream());
outStream.write(sb.toString().getBytes());
// 发送文件数据
if (files != null)
for (Map.Entry<String, File> file : files.entrySet()) {
StringBuilder sb1 = new StringBuilder();
sb1.append(PREFIX);
sb1.append(BOUNDARY);
sb1.append(LINEND);
sb1.append("Content-Disposition: form-data; name=\"upload\"; filename=\""
+ file.getValue().getName() + "\"" + LINEND);
sb1.append("Content-Type: application/octet-stream; charset=" + CHARSET + LINEND);
sb1.append(LINEND);
outStream.write(sb1.toString().getBytes());
InputStream is = new FileInputStream(file.getValue());
byte[] buffer = new byte[1024];
int len = 0;
while ((len = is.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
is.close();
outStream.write(LINEND.getBytes());
}
// 请求结束标志
byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes();
outStream.write(end_data);
outStream.flush();
// 得到响应码
int res = conn.getResponseCode();
InputStream in = conn.getInputStream();
StringBuilder sb2 = new StringBuilder();
if (res == 200) {
int ch;
while ((ch = in.read()) != -1) {
sb2.append((char) ch);
}
}
outStream.close();
conn.disconnect();
return sb2.toString();
}
}
本次讨论的是第一个方法,即uploadFile方法。本人在最初使用这个方法上传图片时,把此方法体的大部分内容用一个线程(后来去掉了,上下边的代码注释掉)去执行网络请求操作(上传文件到服务器端),在获得请求结果时用本类中的一个静态变量result来接收并作为方法返回值返回。问题就在这里了,静态变量result在线程体内部被赋值了,在返回前却是null值,后来发现是线程的问题。即使是本类中的静态变量,在另一线程中被赋值了,在此线程结束后就被清空掉了。解决方案是,在此方法中去掉线程。在调用此方法处加入线程。代码如下:
new Thread(){
@Override
public void run() {
//上传该图片到服务器
try {
File appDir = new File(Environment.getExternalStorageDirectory(), Constants.APP_LOCAL_STORE);
if (!appDir.exists()) {
appDir.mkdir();
}
File file = new File(appDir, "/portrait.jpg");
LOG.i(TAG,file.getName());
String requestURL = Constants.HOME_URL+"/app/user/uploadFile.htm";
String result = UploadUtil.uploadFile(file,requestURL);
JSONObject json = new JSONObject(result);
JSONObject json1 = json.getJSONObject("result");
LOG.i(TAG,json1.getString("url"));
}catch (Exception e){
e.printStackTrace();
}
}
}.start();
以上代码是写在activity中的一个普通的方法。以上线程为子线程,可以在主线程中用Handle来处理,即监听子线程的状态后在Handle中做相应的处理。
例如,在子线程中调用sendEmptyMessage(int what)方法传入一个标识,然后在Handle的handleMessage方法中做相应处理。代码如下:
//发送消息,通知UI组件显示图片
handler.sendEmptyMessage(0x9527);
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what==0x9527) {
//显示从网上下载的图片
imageview.setImageBitmap(bitmap);
}
}
};
下面是服务器端代码:
//上传文件
@RequestMapping(value="/app/user/uploadFile.htm",method = RequestMethod.POST)
public String uploadFile(@RequestParam("upload") MultipartFile upload){
logger.debug("uploadFile...");
return execute(()->{
if(!upload.isEmpty()){
//上传文件路径
String uploadPath = request.getServletContext().getRealPath("").replace(Constants.PROJECT_NAME, Constants.UPLOAD)+File.separator+Constants.PROJECT_NAME;
//上传文件名
String uploadFileName = upload.getOriginalFilename();
String uploadFileType = uploadFileName.substring(uploadFileName.lastIndexOf("."),uploadFileName.length());
String filename = UUID.randomUUID().toString();
File file = new File(uploadPath , uploadFileName);
//判断路径是否存在,如果不存在就创建一个
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
upload.transferTo(new File(uploadPath + File.separator + filename+"."+uploadFileType));
logger.debug("uploadPath:"+uploadPath);
// FileUtils.copyFile(new File(uploadPath,filename), file);
return ok(JsonResult.jsonResultSuccess("url",Constants.HOST+File.separator+Constants.UPLOAD+File.separator+Constants.PROJECT_NAME+File.separator+filename+uploadFileType));
}else{
return ok(JsonResult.jsonResultError("上传文件失败"));
}
});
成功则返回文件在服务器上的url;失败则提示。