zoey
点击国内主页可以让浏览速度加快哦 ~ !

Android端选择本地视频并上传到SpringMVC服务端

2019-09-28 android studio Android Spring SpringMVC http上传文件 安卓文件真实路径 406错误 多依赖报错
Word count: 4.3k | Reading time: 22min


    这几天做实习项目遇到的安卓端(客户端)选择本地文件(演示对象为视频)上传到web端(即:SpringMVC服务端)的一个需求。首先是android调用系统的选择本地文件界面,然后在重写的函数onActivityResult()中去获取到文件的Uri地址,在利用这个Uri地址进行文件的上传操作。服务端是实习的带队老师帮忙搭建的一个SpringMVC的web端吧,利用controller实现把上传的文件获取并保存到web端的指定目录下。感觉可以记录一下。



p.s>  本来项目内用websocket连接上了服务端的,准备用websocket直接上传文件的,但是由于之前没用过这个websocket技术(博主是个小菜瓜大佬轻喷),出现了一个好像是缓存buffer不足或者未开启异步发送byte[]文件的报错,试了一下没做出来wesocket的文件上传,所以转而转向SpringMVC的上传,如果有用websocket实现过android上传文件到服务端的大佬可以指教或者交流一下~


服务端

    具体的服务端配置是我从实习的指导老师那直接拿过来用的,在此基础上添加的代码内容,所以怎么创建一个SpringMVC的服务端就不详细说了,这里就只说一下涉及的controler和实体类,以及实际遇到的返回结果无法转化为json的问题。这里放一下服务端demo参考的 springmvc服务端+android客户端的文件上传 博客

(后面我又自己趁着假期把服务端自己搭建了一遍:主要是自己搭建一个SpringMVC项目出来并且给搭建上hibernate就行用eclipse搭建自己的SpringMVC+hibernate小demo,ps这个链接的项目有一点不一样:我自己搭建的是用的MySQL而不是postgresql,其次自己搭建的项目是基于Maven的,所以导包操作可以在pom.xml中完成)

服务端配置

这里放一下jar包的下载地址

项目目录
红色框内是本次的相关项

    详细的SpringMVC项目的创建我就不再赘述了,百度应该有一大把的博客有的。

所需上传的jar包

这里我用的jackson的相关jar是2.9.8版本————

    jackson-annotations-2.9.8.jar

    jackson-core-2.9.8.jar

    jackson-databind-2.9.8.jar

上传文件的jar包————

    commons-fileupload-1.2.2.jar

    commons-io-2.6.jar

文件下载下来后直接Ctrl C+V到WebContent/WEB-INF/lib里就行了

(之前并没有开发过SpringMVC项目,而且这次拿到的项目,老师也没有用maven以及porm.xml,所以拿到项目后我是直接导入的,如果我说的有不对的地方还请指正)

springmvc.xml配置

springmvc.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

<mvc:annotation-driven />

<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.spring.handlers.controller"></context:component-scan>


<!-- 配置视图解析器 如何把handler 方法返回值解析为实际的物理视图 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name = "prefix" value="/WEB-INF/views/"></property>
<property name = "suffix" value = ".jsp"></property>
</bean>

<!-- 多部分文件上传 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"></property>
<property name="maxUploadSize" value="10485760000"></property>
<property name="maxInMemorySize" value="40960"></property>
</bean>


</beans>

实体类

用一个result的实体类来返回post的响应结果

result实体类代码

result.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import org.springframework.stereotype.Component;

@Component
public class Result {
private String path;
private int code;
private String message;


public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public void setMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}



Controller

    添加一个UploadController来实现对android端post请求的接受

UploadController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.spring.handlers.controller;
import java.io.File;
import java.io.IOException;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

@Controller

public class UploadController {

@Resource
private Result result;

@RequestMapping(value ="/mobile/uploadfile" , produces = "application/json;charset=utf-8")
@ResponseBody //把回传类转换成json
public Result uploadPhone(@RequestParam(value="file",required=false)MultipartFile file,HttpServletRequest request) throws IllegalStateException, IOException{
System.out.println("进入uploadPhone方法");
String path=uploadFile(file, request);
result.setCode(200);
result.setPath(path);
System.out.println(path);
result.setMessage("上传成功");
System.out.println(result.getMessage());
return result;

}

/**
* 上传
* @param file
* @param request
* @return
* @throws IOException
*/
private String uploadFile(MultipartFile file,HttpServletRequest request) throws IOException {
System.out.println("进入uploadFile方法");
String path=request.getSession().getServletContext().getRealPath("upload");
String fileName=file.getOriginalFilename();
File targetFile=new File(path,fileName);
if(!targetFile.exists()){
targetFile.mkdirs();
}
file.transferTo(targetFile);
// request.setAttribute("filePath",targetFile.getAbsolutePath());
return targetFile.getAbsolutePath();
}

}

android端

添加权限

添加网络访问权限

在AndroidManifest.xml中,manifest标签中添加权限:

1
<uses-permission android:name="android.permission.INTERNET" />

添加文件存储访问权限

在AndroidManifest.xml中,manifest标签中添加权限:

1
2
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

AndroidManifest.xml文件

AndroidManifest.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.uploadtest">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>

android选择本地文件

    这里主要用到的就是隐式Intent,实现android调用系统文件选择器,并在重写的函数onActivityResult()里去获取文件的Uri值,例如: content://com.android.providers.media.documents/document/video%3A184539

选择文件的前端代码

activity_main.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">

<TextView
android:id="@+id/show_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="上传文件测试"
/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="ChooseVideo"
android:text="选择视频文件" />

</LinearLayout>

android选择本地文件的Activity代码

MainActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.example.uploadtest;

import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import com.example.uploadtest.myUtils.AndroidUploadFile;
import com.example.uploadtest.myUtils.FileTool;

import org.apache.http.client.ClientProtocolException;

import java.io.File;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {
private final int CHOOSE_VIDEO=0x0b;
private Uri fileUri;
private TextView tv;
private String Host = "192.168.x.xxx";


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

tv=(TextView)findViewById(R.id.show_info);
}

public void ChooseVideo(View v){
Intent chooseVideo = new Intent(Intent.ACTION_OPEN_DOCUMENT);//
chooseVideo.setDataAndType(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "video/*");
chooseVideo.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
startActivityForResult(chooseVideo, CHOOSE_VIDEO);
}

@SuppressLint("MissingSuperCall")
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent){
switch (requestCode){
case CHOOSE_VIDEO://选择本地视频
if(resultCode==RESULT_OK){
if(null != intent.getData()){
try {
fileUri = intent.getData();
Log.i("URI",fileUri.toString());
tv.setText(fileUri.toString());
//这里一会放接着的文件代码↓

//这里一会放接着的文件代码↑
}catch (Exception e){e.printStackTrace();}
}else{
Log.e("onActivityResult","intent.getData()回调数据为空!!!!!!");
}
}
break;
}
}

}

文件处理类

    之前我们实现的是调用android系统的视频文件选择器,选择并返回了文件的Uri地址,但是返回的仅仅是Uri地址,并非真实地址,这里我放一个文件Uri地址转真实地址的文件处理类FileTool类,用来把得到的Uri转化为对应文件的真实地址,
例如:
之前的Uri地址:content://com.android.providers.media.documents/document/video%3A184539,
利用这个类转化的真实路径为:/storage/emulated/0/Pictures/Screenshots/SVID_20190726_092605_1.mp4。

FileTool.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
package com.example.uploadtest.myUtils;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class FileTool {


public static File getFileFromUri(Uri uri, Context context) {
if (uri == null) {
return null;
}
switch (uri.getScheme()) {
case "content":
return getFileFromContentUri(uri, context);
case "file":
return new File(uri.getPath());
default:
return null;
}
}
/**
* Gets the corresponding path to a file from the given content:// URI
*
* @param contentUri The content:// URI to find the file path from
* @param context Context
* @return the file path as a string
*/

private static File getFileFromContentUri(Uri contentUri, Context context) {
if (contentUri == null) {
return null;
}
File file = null;
String filePath = new String();
String fileName;
String[] filePathColumn = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME};
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(contentUri, filePathColumn, null,
null, null);
if (cursor != null) {
cursor.moveToFirst();
try{
//filePath = cursor.getString(cursor.getColumnIndex(filePathColumn[0]));
filePath = getFilePathByUri(context,contentUri);
}catch(Exception e){
}
fileName = cursor.getString(cursor.getColumnIndex(filePathColumn[1]));
cursor.close();
Log.e("filePath",filePath);
Log.e("fileName","\nfilePathColumn[1]:"+filePathColumn[1]);
if (!TextUtils.isEmpty(filePath)) {
file = new File(filePath);
}
if (!file.exists() || file.length() <= 0 || TextUtils.isEmpty(filePath)) {
Log.e("测试","记删");
filePath = getPathFromInputStreamUri(context, contentUri, fileName);
}
if (!TextUtils.isEmpty(filePath)) {
file = new File(filePath);
}
}
return file;
}

/**
* 用流拷贝文件一份到自己APP目录下
*
* @param context
* @param uri
* @param fileName
* @return
*/
public static String getPathFromInputStreamUri(Context context, Uri uri, String fileName) {
InputStream inputStream = null;
String filePath = null;

if (uri.getAuthority() != null) {
try {
inputStream = context.getContentResolver().openInputStream(uri);
File file = createTemporalFileFrom(context, inputStream, fileName);
filePath = file.getPath();

} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (Exception e) {
e.printStackTrace();
Log.e("FileTool",e.toString());
}
}
}

return filePath;
}

private static File createTemporalFileFrom(Context context, InputStream inputStream, String fileName)
throws IOException {
File targetFile = null;

if (inputStream != null) {
int read;
byte[] buffer = new byte[32 * 1024];
//自己定义拷贝文件路径
targetFile = new File(fileName);//getCacheDir(context),
if (targetFile.exists()) {
targetFile.delete();
}
OutputStream outputStream = new FileOutputStream(targetFile);

while ((read = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, read);
}
outputStream.flush();

try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}

return targetFile;
}

//文件转byte[]
public static byte[] uriTobytes(final Uri uri,Context context) {
Log.e("uriTobytes","进入uriTobytes方法");
byte[] data = null;
//File file = getFileFromContentUri(uri,AnimalsDiscoverActivity.this);
try {
FileInputStream fileInputStream = new FileInputStream(getFileFromContentUri(uri,context));
int length = fileInputStream.available();
data = new byte[length];
fileInputStream.read(data);
fileInputStream.close();
}catch (Exception e){
e.printStackTrace();
Log.e("uriTobytes",e.toString());
}
return data;
}

//获取文件路径 FilePath
public static String getFilePathByUri(Context context, Uri uri) {
String path = null;
// 以 file:// 开头的
if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
path = uri.getPath();
return path;
}
// 以 content:// 开头的,比如 content://media/extenral/images/media/17766
if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.Media.DATA}, null, null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
if (columnIndex > -1) {
path = cursor.getString(columnIndex);
}
}
cursor.close();
}
return path;
}
// 4.4及之后的 是以 content:// 开头的,比如 content://com.android.providers.media.documents/document/image%3A235700
if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (DocumentsContract.isDocumentUri(context, uri)) {
if (isExternalStorageDocument(uri)) {
// ExternalStorageProvider
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
path = Environment.getExternalStorageDirectory() + "/" + split[1];
return path;
}
} else if (isDownloadsDocument(uri)) {
// DownloadsProvider
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),
Long.valueOf(id));
path = getDataColumn(context, contentUri, null, null);
return path;
} else if (isMediaDocument(uri)) {
// MediaProvider
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{split[1]};
path = getDataColumn(context, contentUri, selection, selectionArgs);
return path;
}
}
}
return null;
}

private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {column};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}

private static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}

private static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}

private static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
}

然后就可以在MainActivity.java内注释的地方加入代码调用FileTool类处理Uri地址了:

1
2
String path= FileTool.getFilePathByUri(this,fileUri);
Log.e("打印path",path);

文件上传类

    之后需要一个AndroidUploadFile类,利用http的post请求把文件推到服务端去,并且利用返回消息在android端判断是否有上传成功。

在app中添加依赖

因为上传文件时需要用到http请求,所以在app的gradle导入一下apache的http依赖项:

1
implementation 'org.apache.httpcomponents:httpmime:4.5.10'

放一下build.gradle(app)的代码:

build.gradle(app)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
apply plugin: 'com.android.application'

android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.example.uploadtest"
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
}

}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'org.apache.httpcomponents:httpmime:4.5.10'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

AndroidUploadFile类

AndroidUploadFile.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package com.example.uploadtest.myUtils;

import android.util.Log;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ContentBody;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.util.EntityUtils;

import java.io.File;
import java.io.IOException;

public class AndroidUploadFile {
/**
*
* @param filePath 文件地址
* @param urlServer 服务器的地址
* @return
* @throws IOException
* @throws ClientProtocolException
*/
public static String uploadFile(String filePath,String urlServer) throws ClientProtocolException, IOException, IOException {

//使用HttpClient
HttpClient httpClient=new DefaultHttpClient();

//必须设置请求的协议
httpClient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);

//设置http post请求
HttpPost httpPost=new HttpPost(urlServer);

//构建上传的文件的实体
MultipartEntity multipartEntity=new MultipartEntity();

//构建文件的File的对象
File file=new File(filePath);

//添加文件的
ContentBody contentBody=new FileBody(file);
multipartEntity.addPart("file",contentBody);//<input type="file" name="file">

//把请求实体设置到HttpPost
httpPost.setEntity(multipartEntity);

//执行这个请求
HttpResponse response=httpClient.execute(httpPost);
Log.e("我再打印一下",response.toString());
//通过响应取到状态行
StatusLine statusLine= response.getStatusLine();
//通过状态码去判断
if(statusLine.getStatusCode() == HttpStatus.SC_OK){
HttpEntity entity=response.getEntity();
String result= EntityUtils.toString(entity);
Log.i("TAG","*******"+result);
}else{
HttpEntity entity=response.getEntity();
String result= EntityUtils.toString(entity);
Log.i("TAG","*******"+result);
Log.e("StatusCode","\n statusLine.getStatusCode():"+ statusLine.getStatusCode());
Log.i("TAG","请求出了问题");
}
return null;
}

}

并在MainActivity中调用

1
AndroidUploadFile.uploadFile(file.getAbsolutePath(), "http://"+Host+":8080/PortalSystemAdmin/mobile/uploadfile");

贴一下完整的MainActivity.class代码

MainActivity.class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package com.example.uploadtest;

import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import com.example.uploadtest.myUtils.AndroidUploadFile;
import com.example.uploadtest.myUtils.FileTool;

import org.apache.http.client.ClientProtocolException;

import java.io.File;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {
private final int CHOOSE_VIDEO=0x0b;
private Uri fileUri;
private TextView tv;
private String Host = "192.168.x.xxx";//填web端的ip地址,即你自己的ip地址


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

tv=(TextView)findViewById(R.id.show_info);
}

public void ChooseVideo(View v){
Intent chooseVideo = new Intent(Intent.ACTION_OPEN_DOCUMENT);//
chooseVideo.setDataAndType(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "video/*");
chooseVideo.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
startActivityForResult(chooseVideo, CHOOSE_VIDEO);
}

@SuppressLint("MissingSuperCall")
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent){
switch (requestCode){
case CHOOSE_VIDEO://选择本地视频
if(resultCode==RESULT_OK){
if(null != intent.getData()){
try {
fileUri = intent.getData();
Log.i("URI",fileUri.toString());
tv.setText(fileUri.toString());
String path= FileTool.getFilePathByUri(this,fileUri);//真实路径
Log.e("打印path",path);
final File file=new File(path);//利用真实路径创建File
new Thread(){//android中耗时操作现在必须放到线程中去做,不然会报错
public void run() {
try {
// 调用写的上传工具类
AndroidUploadFile.uploadFile(file.getAbsolutePath(), "http://"+Host+":8080/PortalSystemAdmin/mobile/uploadfile");
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
}.start();
}catch (Exception e){e.printStackTrace();}
}else{
Log.e("onActivityResult","intent.getData()回调数据为空!!!!!!");
}
}
break;
}
}

}

运行结果以及总结

途中遇到的问题和解决方案

实体类无法转化json问题

开始视频上传是能成功的,但是android端接受不到请求返回的json消息,并且总是会报406错误
然而回去看,代码里确实是有 @ResponseBody注解的,这个注解就是实体类转化为json回传给android端
结果仔细检查后发现主要是自己的json依赖包没有导入(卒)
然后在springmvc.xml中加一句:

1
<mvc:annotation-driven />

之后再在lib目录下导入jackson-annotations-2.9.8.jar、jackson-core-2.9.8.jar、jackson-databind-2.9.8.jar这3个依赖包后解决406问题

同样的依赖路径存在多个

遇到的报错:
More than one file was found with OS independent path ‘META-INF/DEPENDENCIES’
添加如下代码后,报错消失:

1
2
3
4
5
6
7
android {
...
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
}

}

结果截图

android控制台

web服务端控制台

上传成功的视频文件

参考博客

博主 molly Hu 的springmvc服务端+android客户端的文件上传

博主 zhangphil 的Android Stduio报错:More than one file was found with OS independent path ‘META-INF/DEPENDENCIES’

Author: Zoey

Link: https://zoey1038569979.github.io/2019/09/28/android_SpringMVC_fileUpload/

Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.

< PreviousPost
用eclipse搭建自己的基于Maven+SpringMVC+hibernate小demo
NextPost >
CCF&&CSP 2017-09 (前2道)
CATALOG
  1. 1. 服务端
    1. 1.1. 服务端配置
      1. 1.1.1. 所需上传的jar包
      2. 1.1.2. springmvc.xml配置
    2. 1.2. 实体类
      1. 1.2.1. result实体类代码
    3. 1.3. Controller
  2. 2. android端
    1. 2.1. 添加权限
      1. 2.1.1. 添加网络访问权限
      2. 2.1.2. 添加文件存储访问权限
      3. 2.1.3. AndroidManifest.xml文件
    2. 2.2. android选择本地文件
      1. 2.2.1. 选择文件的前端代码
      2. 2.2.2. android选择本地文件的Activity代码
      3. 2.2.3. 文件处理类
      4. 2.2.4. 文件上传类
        1. 2.2.4.1. 在app中添加依赖
        2. 2.2.4.2. AndroidUploadFile类
      5. 2.2.5. 贴一下完整的MainActivity.class代码
  3. 3. 运行结果以及总结
    1. 3.1. 途中遇到的问题和解决方案
      1. 3.1.1. 实体类无法转化json问题
      2. 3.1.2. 同样的依赖路径存在多个
    2. 3.2. 结果截图
    3. 3.3. 参考博客