android 上传图片到后端,刚开始学android 感觉好难啊,一步一个坑,而且网上的大多数资料都很老,安卓更新的又快,不像javaweb开发,不用管发布年限,找到方法就行,android这玩意有的方法启用了等等特麻烦android转androidx啊,对新手特不友好,废话少说,开始进入正题
第一步引入网络
//请求网络相关
//okhttp
//https://github.com/square/okhttp
implementation 'com.squareup.okhttp3:okhttp:4.2.0'
//用来打印okhttp请求日志
//当然也可以自定义
implementation("com.squareup.okhttp3:logging-interceptor:4.2.0")
//retrofit
//https://github.com/square/retrofit
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
//使用gson解析json
//https://github.com/google/gson
implementation 'com.google.code.gson:gson:2.8.5'
//适配retrofit使用gson解析
//版本要和retrofit一样
implementation 'com.squareup.retrofit2:converter-gson:2.6.2'
//适配retrofit支持rxjava
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.6.2'
//使用了Android响应式编程
//RxJava和RxAndroid区别?
//简单来说:就是RxAndroid在RxJava的基础上
//优化了一些功能
//增强了Android特有的功能
//https://github.com/ReactiveX/RxAndroid
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
//end 请求网络相关
<!-- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
加入
android:usesCleartextTraffic="true" 不然无法访问网络
封装网络接口
定义一个api
package com.ynrd.tycoon.api;
import com.ynrd.tycoon.domain.Ad;
import com.ynrd.tycoon.domain.Session;
import com.ynrd.tycoon.domain.User;
import com.ynrd.tycoon.domain.UserMessage;
import com.ynrd.tycoon.domain.response.DetailResponse;
import com.ynrd.tycoon.domain.response.ListResponse;
import com.ynrd.tycoon.utils.Constant;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
public class Api {
private final Service service;
/**
* 返回当前对象的唯一实例
*
* 单例设计模式
* 由于移动端很少有高并发
* 所以这个就是简单判断
*
* @return
*/
public static Api getInstance() {
Api instance = null;
if (instance == null){
instance = new Api();
}
return instance;
}
public Api() {
//初始化okhttp
OkHttpClient.Builder okhttpClientBuilder = new OkHttpClient.Builder();
//初始化retrofit
Retrofit retrofit = new Retrofit.Builder()
//让retrofit使用okhttp
.client(okhttpClientBuilder.build())
//api地址
.baseUrl(Constant.ENDPOINT)
//适配rxjava
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
//使用gson解析json
//包括请求参数和响应
.addConverterFactory(GsonConverterFactory.create())
//创建retrofit
.build();
//创建service
service = retrofit.create(Service.class);
}
/**
* 用户注册
* @param user
* @return
*/
public Observable<DetailResponse<Session>> register(User user){
return service.register(user)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
/**
* 用户注册
* @param user
* @return
*/
public Observable<DetailResponse<Session>> perfectMessage(UserMessage user){
return service.perfectMessage(user)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
/**
* 上传头像
* @param file
* @return
*/
public Observable<DetailResponse<Session>> updateImage(RequestBody file){
return service.updateImage(file)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
/**
* 发送验证码
* @return
*/
public Observable<DetailResponse<Session>> verificationCode(User user){
return service.verificationCode(user)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
public Observable<DetailResponse<Session>> login(User user){
return service.login(user)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
/**
* 广告列表
* @return
*/
public Observable<ListResponse<Ad>> ads(){
return service.ads()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
}
定义接口
package com.ynrd.tycoon.api;
import com.ynrd.tycoon.domain.Ad;
import com.ynrd.tycoon.domain.Session;
import com.ynrd.tycoon.domain.User;
import com.ynrd.tycoon.domain.UserMessage;
import com.ynrd.tycoon.domain.response.DetailResponse;
import com.ynrd.tycoon.domain.response.ListResponse;
import io.reactivex.Observable;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
public interface Service {
/**
* 用户注册
* @param data
* @return
*/
@POST("userdata/register")
Observable<DetailResponse<Session>> register(@Body User data);
/**
* 登录
* @param data
* @return
*/
@POST("v1/sessions")
Observable<DetailResponse<Session>> login(@Body User data);
/**
* 广告列表
* @return
*/
@GET("v1/ads")
Observable<ListResponse<Ad>> ads();
/**
* 发送验证码
* @param user
* @return
*/
@POST("userdata/sendSmsValid")
Observable<DetailResponse<Session>> verificationCode(@Body User user);
/**
* 完善用户信息
* @param user
* @return
*/
@POST("userdata/perfectMessage")
Observable<DetailResponse<Session>> perfectMessage(@Body UserMessage user);
/**
* 上传头像
* @param file
* @return
*/
@POST("userdata/updateImage")
Observable<DetailResponse<Session>> updateImage(@Body RequestBody file);
}
在方法里调用请求
private void UpLoadIcon(File file) {
//传值
MultipartBody.Builder builder = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("id", "15")
.addFormDataPart("name", "name");
//如果是多个file[]
builder.addFormDataPart("file", file.getName(), RequestBody.create(MediaType.parse("image/*"), file));
RequestBody requestBody = builder.build();
Api.getInstance().updateImage(requestBody)
.subscribe(new HttpObserver<DetailResponse<Session>>() {
@Override
public void onSucceeded(DetailResponse<Session> data) {
//成功走这里
}
});
}
如果上传多个 参考
https://www.jianshu.com/p/e4a7478689ce
之后是我定义的返回类型和错误判断
package com.ynrd.tycoon.listener;
import com.ynrd.tycoon.activity.BaseCommonActivity;
import com.ynrd.tycoon.domain.response.BaseResponse;
import com.ynrd.tycoon.utils.HttpUtil;
import com.ynrd.tycoon.utils.LoadingUtil;
import io.reactivex.disposables.Disposable;
import retrofit2.Response;
/**
* 网络请求observer
* @param <T>
*/
public abstract class HttpObserver<T> extends ObserverAdapter<T> {
private static final String TAG = "HttpObserver";
/**
* 是否显示加载对话框
*/
private boolean isShowLoading;
/**
* 界面
*/
private BaseCommonActivity activity;
/**
* 无参构造方法
*/
public HttpObserver (){
}
/**
* 构造方法
* @param activity
* @param isShowLoading
*/
public HttpObserver(BaseCommonActivity activity, boolean isShowLoading){
this.activity = activity;
this.isShowLoading = isShowLoading;
}
/**
* 请求成功
* @param data
*/
public abstract void onSucceeded(T data);
/**
* 请求失败
* @param data
* @param e
* @return
*/
public boolean onFailed(T data,Throwable e){
return false;
}
@Override
public void onSubscribe(Disposable d) {
super.onSubscribe(d);
if (isShowLoading){
LoadingUtil.showLoading(activity);
}
}
@Override
public void onNext(T t) {
super.onNext(t);
//检查是否需要隐藏加载提示框
checkHideLoading();
if (isSucceeded(t)){
//请求正常
onSucceeded(t);
}else{
handlerRequest(t,null);
}
}
@Override
public void onError(Throwable e) {
super.onError(e);
// LogUtil.d(TAG, "onError:" + e.getLocalizedMessage());
//检查是否需要隐藏加载提示框
checkHideLoading();
//处理错误
handlerRequest(null, e);
}
/**
* 网络请求是否成功了
*
* @param t
* @return
*/
private boolean isSucceeded(T t) {
if (t instanceof Response) {
//retrofit里面的响应对象
//获取响应对象
Response response = (Response) t;
//获取响应码
int code = response.code();
//判断响应码
if (code >= 200 && code <= 299) {
//网络请求正常
return true;
}
} else if (t instanceof BaseResponse) {
//判断具体的业务请求是否成功
BaseResponse response = (BaseResponse) t;
//状态码为200表示成功
//这是我们和服务端的一个规定
return response.getStatus() == 200;
}
return false;
}
/**
* 处理错误网络请求
*
* @param data
* @param error
*/
private void handlerRequest(T data, Throwable error) {
if (onFailed(data, error)) {
//回调了请求失败方法
//并且该方法返回了true
//返回true就表示外部手动处理错误
//那我们框架内部就不用做任何事情了
} else {
HttpUtil.handlerRequest(data, error);
}
}
/**
* 检查是否需要隐藏加载提示框
*/
private void checkHideLoading() {
if (isShowLoading) {
LoadingUtil.hideLoading();
}
}
}
package com.ynrd.tycoon.listener;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
/**
* 通用实现observer里面的方法
* @param <T>
*/
public class ObserverAdapter<T> implements Observer<T> {
/**
* 开始定义了执行
* @param d
*/
@Override
public void onSubscribe(Disposable d) {
}
/**
* 当前observer
* @param t
*/
@Override
public void onNext(T t) {
}
/**
* 执行失败了
* @param e
*/
@Override
public void onError(Throwable e) {
}
/**
* 调用onNext方法后调用
*/
@Override
public void onComplete() {
}
}
package com.ynrd.tycoon.domain.response;
/**
* 通用网络请求响应模型
*/
public class BaseResponse {
/**
* 状态码
* 只有发生了错误才会有值
*/
private int status;
/**
* 出错的提示信息
* 发生了错误不一定有
*/
private String message;
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
package com.ynrd.tycoon.domain.response;
/**
* 通用网络请求响应模型
*/
public class BaseResponse {
/**
* 状态码
* 只有发生了错误才会有值
*/
private int status;
/**
* 出错的提示信息
* 发生了错误不一定有
*/
private String message;
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
package com.ynrd.tycoon.domain.response;
/**
* 详情网络请求解析类
* 继承BaseResponse
* 定义了一个泛型T
* @param <T>
*/
public class DetailResponse<T> extends BaseResponse {
/**
* 真实的数据 类型是泛型
*/
private T data;
private String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
package com.ynrd.tycoon.domain.response;
import java.util.List;
/**
* 解析列表网络请求
* @param <T>
*/
public class ListResponse<T> extends BaseResponse {
/**
* 定义一个列表
* 里面的对象使用了泛型
*/
private List<T> data;
public List<T> getData() {
return data;
}
public void setData(List<T> data) {
this.data = data;
}
}
package com.ynrd.tycoon.utils;
import com.ynrd.tycoon.BuildConfig;
public class Constant {
/**
* 端口
*/
public static final String ENDPOINT = BuildConfig.ENDPOINT;
public static final String RESOURCE_ENDPOINT = BuildConfig.RESOURCE_ENDPOINT;
}
//配置不同的环境
productFlavors {
//开发环境
dev {
buildConfigField('String', "ENDPOINT", '"http://192.168.1.124"')
//资源断点
//资源端点
buildConfigField 'String', 'RESOURCE_ENDPOINT', '"http://dev-courses-misuc.ixuea.com/%s"'
dimension = minSdkVersion
}
//正式环境
prod {
//API端点
buildConfigField 'String', 'ENDPOINT', '"http://api-courses-misuc.ixuea.com/"'
//资源断点
//资源端点
buildConfigField 'String', 'RESOURCE_ENDPOINT', '"http://dev-courses-misuc.ixuea.com/%s"'
dimension = minSdkVersion
}
}
加入这段话不然会报错
flavorDimensions "versionCode"
没看明白的同学可以都复制到自己项目里试一试,都复制进去应该可以运行成功,有问题可以留言解决
springboot如何接收
/**
* 接收安卓图片接口
* @param request
* @return
*/
@RequestMapping(value = "/updateImage", method = RequestMethod.POST)
@ResponseBody
public JSONResult handleFileUpload(HttpServletRequest request) {
MultipartHttpServletRequest params=((MultipartHttpServletRequest) request);
List<MultipartFile> files = ((MultipartHttpServletRequest) request)
.getFiles("file");
String name=params.getParameter("name");
System.out.println("name:"+name);
String id=params.getParameter("id");
System.out.println("id:"+id);
MultipartFile file = null;
BufferedOutputStream stream = null;
for (int i = 0; i < files.size(); ++i) {
file = files.get(i);
if (!file.isEmpty()) {
try {
String filePath = Global.getUploadPath();
// 上传并返回新文件名称
String fileName = FileUploadUtils.upload(filePath, file);
String url = serverConfig.getUrl() + fileName;
System.out.println(url);
} catch (Exception e) {
stream = null;
return JSONResult.errorMsg("服务器异常");
}
} else {
return JSONResult.errorMsg("图片上传失败");
}
}
return JSONResult.build(500,"没有上传图片",new User());
}