安卓上传图片到服务器阿里云OSS(可实现多图片上传)
开发过程中遇到的问题
1、W/System.err: java.net.UnknownServiceException:
报错
W/System.err: java.net.UnknownServiceException:
CLEARTEXT communication to xxx.xxx.xxx not permitted by network security policy
问题分析:
为保证用户数据和设备的安全,Google针对下一代 Android 系统(Android P) 的应用程序,将要求默认使用加密连接,这意味着 Android P 将禁止 App 使用所有未加密的连接,因此运行 Android P 系统的安卓设备无论是接收或者发送流量,未来都不能明码传输,需要使用下一代(Transport Layer Security)传输层安全协议,而 Android Nougat 和 Oreo 则不受影响。
在Android P系统的设备上,如果应用使用的是非加密的明文流量的http网络请求,则会导致该应用无法进行网络请求,https则不会受影响,同样地,如果应用嵌套了webview,webview也只能使用https请求。
解决方法:往
AndroidManifest
的
application
中设置下列属性即可。
android:usesCleartextTraffic="true"
一、效果演示
这次主要是实现本地图片上传到服务器的功能,如下
gif
所示:上传图片之后,就可以通过图片链接来访问到图片,并在页面展示出来。
上传成功之后可以在服务器查看到该图片了,如下图所示:
二、购买阿里云OSS对象存储
直接在阿里云首页搜索
对象存储OSS
点击
图中所示的地方
我们选择
标准存储包
即可,然后选择
中国大陆
地区,存储包规格选个
40G
够用了,因为只是存储博客图片,或者是笔记图片都是够了的。然后时长按自己所需来选即可,反正也不贵。流量的话因为我就上次一点图片,所以按量计费不是很贵。
(咳咳,存储空间虽然不贵,但是外网访问图片资源时下载图片资源不免费,这是真滴坑,值得一提的是上传图片免费,具体可以查看相关资料)
三、设置阿里云OSS对象存储配置
1. 新建Bucket
2. 配置Bucket
按图中选择即可,因为我也是服务器小白,所以某些设置可能有问题,但是毕竟这只是一个学习的demo,设置好Bucket之后能使用就行,是吧,嘿嘿嘿…
然后点击进入该
Bucket
,创建几个存放图片的文件夹,这里我创建了一个存放头像的文件夹
usericons
,以及创建一个存放用户发布的图片的文件夹
images
3. 绑定域名(如果有)
如果有域名且域名认证了的话,可以绑定域名。没有就跳过这一步即可。
这里可能会有一个问题:
添加 CNAME 记录提示和 A 记录冲突如何解决
。
解决方法:
添加 CNAME 记录提示和 A 记录冲突解决方法
4. 创建RAM用户获取密匙
点击
AccessKey管理
点这个
创建一个Ram用户
创建好之后点击确认,名称随便填
经过验证之后就创建成功了记得要保存
AccessKey ID
和
AccessKey Secret
, 不保存下次就没了,没了能怎么办?重新删掉,再创建一个呗!!!
给创建的Ram用户赋值, 在用户界面, 点击添加权限
最后赋予图中的权限, 点击确认即可, 这样就完成了配置, 完成了之后我们就需要到代码中去配置了。
(其实这样也是完成了图床的配置, 可以使用我们获取到的
AcessKeyID
和
AcessKey Secret
在
PicGo
软件上配置,这样随时都能上传图片到该服务器, 还可以配合
Typora
将写笔记(博客)的图片上传到该服务器,这里就不写了).
四、Android上传到服务器代码实现
参考文献:
阿里云官网:
上传本地文件到阿里云OSS
1. 引入阿里云OSS依赖
//自动获取最新版本
implementation 'com.aliyun.dpa:oss-android-sdk:+'
2. AliyunUploadFile工具类
这个就是我们上传图片封装的工具类了。我已经写好注释了,你只需要将你的
AccessKey ID
和
AcessKey Secert
等填好,在图片发布Activity中调用该类,将获取到的图片地址传到该类就行了。
public class AliyunUploadFile {
private OSSCredentialProvider credentialProvider;
private ClientConfiguration conf;
private OSS oss;
// 填写生成的访问密钥AccessKey ID和AccessKey Secret。
private String AccessKeyId = "LTAI66GDnSkpScpuJMgt26RJ";
private String SecretKeyId = "U8eEfIlz4f7DTP8hjW4b8FfFUBBxtA";
// STS接入地址,例如sts.cn-hangzhou.aliyuncs.com。
private String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
//bucketName
private String bucketName = "xiaohaituchuang";
/**
*
* @param context 上下文
* @param uploadFilePath 上传的文件路径
*/
public void UploadFile(Context context, String uploadFilePath) {
// 第一个参数:在【RAM访问控制】创建用户时分配的accessKeyId
// 第二个参数:在【RAM访问控制】创建用户时分配的accessKeySecret
OSSCredentialProvider ossCredentialProvider = new OSSPlainTextAKSKCredentialProvider(AccessKeyId, SecretKeyId);
// 第一个参数:上下文
// 第二个参数:在OSS控制台创建好Bucket后,会有一个EndPoint(地域节点),比如我这里的节点是:http://oss-cn-hangzhou.aliyuncs.com
// 第三个参数:OSSCredentialProvider
OSS oss = new OSSClient(context.getApplicationContext(), endpoint, ossCredentialProvider);
String img_name = "images/img_";
//获取图片格式后缀名,比如.png, .jpg等等
String str1 = uploadFilePath.substring(0, uploadFilePath.indexOf("."));
String str2 = "." + uploadFilePath.substring(str1.length()+1,uploadFilePath.length());
//上传后图片的地址, 可以将该图片地址存入数据库,我这里就不写存进数据库了。
String img_url = "http://域名/" + img_name + str2;
img_name = img_name + str2;
// 第二个参数:可以是一个文件路径:比如你在xiaohaituchuang创建了一个文件夹为images,那第二个参数传images/img.jpg
// 第三个参数:是文件的本地路径,比如我这里在本地路径中是姨丈图片,路径是sdcard/img/img.jpg
PutObjectRequest put = new PutObjectRequest(bucketName, img_name, uploadFilePath);
// 异步上传时可以设置进度回调。
put.setProgressCallback((request, currentSize, totalSize) -> {
Log.d("currentSize = " + currentSize, "totalSize = " + totalSize);
});
// 此处调用异步上传方法
OSSAsyncTask ossAsyncTask= oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
@Override
public void onSuccess(PutObjectRequest request, PutObjectResult result) {
Log.d("PutObject", "UploadSuccess");
Log.d("ETag", result.getETag());
Log.d("RequestId", result.getRequestId());
uploadFlag = 1; //这个是上次多张图片时判断上传成功的标准,我在例子里写有,不需要的话可以删除
}
@Override
public void onFailure(PutObjectRequest request, ClientException clientException, ServiceException serviceException) {
// 请求异常。
if (clientException != null) {
// 本地异常,如网络异常等。
Log.e("网络异常",clientException.getMessage());
uploadFlag = 2;
}
if (serviceException != null) {
// 服务异常。
Log.e("ErrorCode", serviceException.getErrorCode());
Log.e("RequestId", serviceException.getRequestId());
Log.e("HostId", serviceException.getHostId());
Log.e("RawMessage", serviceException.getRawMessage());
uploadFlag = 3;
}
}
});
}
}
3. AliyunUploadFile工具类调用小例子,可一次上次多张图片
我这里就简单的对发布按钮进行监听, 当点击的时候调用上传文件工具类对图片进行上传。在
NineGridViewActivity
里面编写:
(1)定义图片集合
private List<File> files; //图片集合
private int[] filesIndex; //图片集合下标
(2)定义图片上传状态:
public static int uploadFlag = 0; //0:表示图片未开始上传, 1:表示图片上传成功
(3)初始化图片集合
在
OnCreate
方法里面初始化即可。
files = new ArrayList<>();
filesIndex = new int[9];//因为我弄的是最多9张图
(4)获取图片地址。
for (int i = 0; i < list.size(); i++) {
File file = new File(list.get(i).getImagePath());
files.add(file);
filesIndex[i] = 1;
}
在这里获取图片地址:
(5)调用工具类,点击发布按钮后,就上传。
这里演示的是在
NineGridViewActivity
中的
OnCreate
调方法用该工具类:
//绑定上传图片按钮
Button button = findViewById(R.id.send_button);
//监听按钮点击事件,点击后上传图片.
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AliyunUploadFile picUpload = new AliyunUploadFile();
Toast.makeText(NineGridViewActivity.this, "上传中,请稍后!!!", Toast.LENGTH_SHORT).show();
//上传图片
try {
for (int i = 0; i < files.size(); i++) {
if(filesIndex[i] != 1) continue;
picUpload.UploadFile(NineGridViewActivity.this,files.get(i).getPath());
//等待该图片上传完成
while (uploadFlag == 0);
if(i < files.size()-1) uploadFlag = 0;
}
} catch (Exception e) {
e.printStackTrace();
}
//等待最后一张图上传完成
while (uploadFlag == 0);
//使用弹窗反馈给用户上传结果
switch (uploadFlag) {
case 1:
Toast.makeText(NineGridViewActivity.this, "上传成功!!!", Toast.LENGTH_SHORT).show();
break;
case 2:
Toast.makeText(NineGridViewActivity.this, "上传失败,网络异常!!!", Toast.LENGTH_SHORT).show();
break;
case 3:
Toast.makeText(NineGridViewActivity.this, "上传失败,服务器异常!!!", Toast.LENGTH_SHORT).show();
break;
case 4:
Toast.makeText(NineGridViewActivity.this, "上传失败,数据库异常!!!", Toast.LENGTH_SHORT).show();
break;
default:
Toast.makeText(NineGridViewActivity.this, "上传失败,未知错误!!!", Toast.LENGTH_SHORT).show();
break;
}
}
});
上传后的图片可以在服务器查看:
最后大功告成,在阿里云官网里控制台的对象存储OSS中可以看到上传的图片,谢谢大家的观看。