今天来讲一下,如何将安卓设备上的小文件上传到java web的服务器上。
首先,如果条件允许需要搭建一局域网,没有条件的话也可以在同一开发电脑上测试
安卓设备上的小文件无法直接上传到服务器上,需要通过一个api来实现功能。因此代码也可以分为两部份,即:服务端(api)和应用端(安卓设备)。
服务端(api)代码实现
1.打开eclipse,打开或新建一个java web网站(请确保你的电脑已经安装Tomcat)。
2.复制并引用三个jar包。commons-fileupload-1.2.1.jar、commons-io-1.4.jar、servlet-api.jar。
3.新建一个类,这里命名为UploadHandleServlet,并写入代码,代码如下:
package firstweb;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadHandleServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全
String savePath = this.getServletContext().getRealPath("/upload");
System.out.println(savePath);
File file = new File(savePath);
//判断上传文件的保存目录是否存在
if (!file.exists() && !file.isDirectory()) {
System.out.println(savePath+"目录不存在,需要创建");
//创建目录
file.mkdir();
}
//消息提示
String message = "";
try{
List<FileItem> list = upload.parseRequest(request);
for(FileItem item : list){
//如果fileitem中封装的是普通输入项的数据
if(item.isFormField()){
String name = item.getFieldName();
//解决普通输入项的数据的中文乱码问题
String value = item.getString("UTF-8");
//value = new String(value.getBytes("iso8859-1"),"UTF-8");
System.out.println(name + "=" + value);
}else{//如果fileitem中封装的是上传文件
//得到上传的文件名称,
String filename = item.getName();
System.out.println(filename);
if(filename==null || filename.trim().equals("")){
continue;
}
//注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如: c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt
//处理获取到的上传文件的文件名的路径部分,只保留文件名部分
filename = filename.substring(filename.lastIndexOf("\\")+1);
//获取item中的上传文件的输入流
InputStream in = item.getInputStream();
//创建一个文件输出流
FileOutputStream out = new FileOutputStream(savePath + "\\" + filename);
//创建一个缓冲区
byte buffer[] = new byte[1024];
//判断输入流中的数据是否已经读完的标识
int len = 0;
//循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
while((len=in.read(buffer))>0){
//使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中
out.write(buffer, 0, len);
}
//关闭输入流
in.close();
//关闭输出流
out.close();
//删除处理文件上传时生成的临时文件
item.delete();
message = "文件上传成功!";
}
}
}catch (Exception e) {
message= "文件上传失败!";
e.printStackTrace();
}
response.setHeader("Content-type", "text/html;charset=UTF-8"); //中文乱码解决
response.getWriter().write(message);
// request.setAttribute("message",message);
// request.getRequestDispatcher("/message.jsp").forward(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
4.项目节点下有一个WebContent节点,在其上面右键new(新建)一个jsp页面,这里为取名为upload代码如下。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>文件上传</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<form action="<%=path %>/UploadHandleServlet" enctype="multipart/form-data" method="post">
上传文件1:<input type="file" name="file1"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
5.继续在WebContent节点新建一个名为message.jsp网页,代码如下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>提示消息</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
${message}
</body>
</html>
6.在WebContent\WEB-INF新建一个配置文件——web.xml。代码如下
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>NetServer</display-name>
<welcome-file-list>
<welcome-file>upload.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>UploadHandleServlet</servlet-name>
<servlet-class>firstweb.UploadHandleServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadHandleServlet</servlet-name>
<url-pattern>/UploadHandleServlet</url-pattern>
</servlet-mapping>
</web-app>
7.保存前面编写的所有文件,运行(run as server)或发布网站。
试着在网页上上传一个文件,如果上传成功,表明服务端代码已全部完成。
应用端(安卓设备)代码实现:
1.打开andoid studio,新建或打开一个项目。
2.引入两个jar包okhttp-3.2.0.jar、okio-1.9.0.jar和一个aar包WaitDialog.aar(用于处理耗时等待,提醒用户,并防止用户在此期间进行新操作,直到所有分线程处理完毕,我为方便将其封装,也可以自己写)。
3.在AndroidManifest.xml,添加权限如下:(如果是安卓10及以上(api 29),需要在application节点添加android:requestLegacyExternalStorage=“true”、 android:usesCleartextTraffic=“true”)。
<!-- 读写存储卡 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- Android10新增权限MANAGE_EXTERNAL_STORAGE -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<!-- 互联网 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 查看网络状态 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
4.新建一个“权限检查的类”,代码如下:
package com.example.scjavaweb.util;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;
import androidx.core.app.ActivityCompat;
public class PermessionUtil
{
public static void CheckPermissions(Activity activity, String[] ArrayPermssion, int requstcode)
{
if (Build.VERSION.SDK_INT>=23) //安卓6以上可以动态授权
{
for (String pressionname:ArrayPermssion )
{
int permission = ActivityCompat.checkSelfPermission(activity,
pressionname);
if (permission != PackageManager.PERMISSION_GRANTED) {
// 请求权限
ActivityCompat.requestPermissions(activity, ArrayPermssion,
requstcode);
}
}
}
}
}
5.新建一个Activity页面,在Activity对应的布局文件中添加一个按钮(布局十分简单,代码略)。
6.在Activity对应的java页面,编写代码如下
package com.example.scjavaweb;
import androidx.appcompat.app.AppCompatActivity;
import android.Manifest;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.example.scjavaweb.util.PermessionUtil;
import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import java.util.UUID;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import swaddle.yinzhenwei.waitdialog.WaitDialog;
public class MainActivity extends AppCompatActivity implements View.OnClickListener
{
private static String[] PERMISSIONS = {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.MANAGE_EXTERNAL_STORAGE
};
private WaitDialog waitDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
PermessionUtil.CheckPermissions(this,PERMISSIONS,1);
setContentView(R.layout.activity_main);
waitDialog=new WaitDialog(this);
findViewById(R.id.Btn).setOnClickListener(this);
}
private ResponseBody upload(String url, String filePath, String fileName) throws Exception
{
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", fileName,
RequestBody.create(MediaType.parse("multipart/form-data"), new File(filePath)))
.build();
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + UUID.randomUUID())
.url(url)
.post(requestBody)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
return response.body();
}
private void uploadtojavaweb(String UploadFilename)
{
Calendar calendar=Calendar.getInstance();
Message message = Message.obtain(); // 获得一个默认的消息对象
String ext=GetExt(UploadFilename);
String fileName=String.valueOf(calendar.getTimeInMillis())+"."+ext;
String url = "http://10.0.2.2:1994/firstweb/UploadHandleServlet";
try
{
//
String str=upload(url, UploadFilename, fileName).string();
Log.v("yzw",str);
message.obj=str;
}
catch (Exception e)
{
Log.v("yzw",e.toString());
message.obj=e.toString();
}
mHandler.sendMessage(message);
}
private void openalbum()
{
Intent albumIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
//albumIntent.addCategory(Intent.CATEGORY_OPENABLE);
albumIntent.setType("image/*"); // 类型为图像
startActivityForResult(albumIntent, 100); // 打开系统相册
}
private String uri2Path(Uri uri) //将URi转为路径
{
int actual_image_column_index;
String img_path;
String[] proj = { MediaStore.Images.Media.DATA };
Cursor cursor = getContentResolver().query(uri, proj, null, null, null);
actual_image_column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
img_path = cursor.getString(actual_image_column_index);
return img_path;
}
private String GetExt(String FileName) //获得扩展名
{
String result="";
if (FileName.length()>0)
{
String[] arrext=FileName.split("\\.");
if (arrext.length>0)
{
result=arrext[arrext.length-1];
}
else
{
result="";
}
}
return result;
}
private Handler mHandler = new Handler()
{
public void handleMessage(Message msg)
{
String tip=String.valueOf(msg.obj);
if (tip.length()>0) {
Toast.makeText(MainActivity.this, tip, Toast.LENGTH_LONG).show();
}
waitDialog.dismiss();
}
};
@Override
public void onClick(View view)
{
openalbum();
}
protected void onActivityResult(int requestCode, int resultCode, Intent intent)
{
super.onActivityResult(requestCode, resultCode, intent);
switch (requestCode)
{
case 100:
Uri uri = intent.getData(); // 获得已选择照片的路径对象
String FileName=uri2Path(uri);
TUpload tupload=new TUpload();
tupload.FileName=FileName;
waitDialog.show();
waitDialog.setText("正将文件上传到服务器,稍候");
tupload.start();
break;
}
}
private class TUpload extends Thread
{
public String FileName="";
public void run()
{
uploadtojavaweb(FileName);
}
}
}
至此,安卓端的代码完成,运行之,在图库中选择一张图片上传,如果上传成功,可以在网站(api)的物理路径看到这张上传的图片。
在编写应用端(安卓设备)代码时有以下几点需要注意。
1.在处理网络操作等耗时,不要在主线程进行(安卓4.0后默认不允许在主线中进行耗时操作),应在分线程中进行处理。
2.安卓分线程中不能编辑UI,但可以访问,如果要编辑UI,可以通过广播或handle.sendmessage与主线程通信,由主线程操作UI。
3.如果AVD模拟器访问本地计算机(即AVD模拟器与服务器处于同一电脑),需要将本地ip127.0.0.1或localhost改为10,0.2.2,127.0.0.1或localhost模拟器会当做其本身。