使用JAVA接入华为OBS云存储的踩坑和解决问题的过程
走过的弯路
- 关于SDK的选择
华为云有一个API Explorer,在左边选择存储,我们可以看到有如下的maven依赖配置
<dependency>
<groupId>com.huaweicloud.sdk</groupId>
<artifactId>huaweicloud-sdk-obs</artifactId>
<version>3.1.122</version>
</dependency>
一开始我也是用了这个SDK,可后来去看Obs相关的接口文档的时候,却又发现使用的并不是这个SDK,而是诸如以下maven依赖项
<dependency>
<groupId>com.huaweicloud.sdk</groupId>
<artifactId>huaweicloud-sdk-ecs</artifactId>
<version>${version}</version>
</dependency>
<dependency>
<groupId>com.huaweicloud.sdk</groupId>
<artifactId>huaweicloud-sdk-vpc</artifactId>
<version>${version}</version>
</dependency>
或者是
<dependency>
<groupId>com.huaweicloud.sdk</groupId>
<artifactId>huaweicloud-sdk-all</artifactId>
<version>${version}</version>
</dependency>
又或者是
<dependency>
<groupId>com.huaweicloud.sdk</groupId>
<artifactId>huaweicloud-sdk-bundle</artifactId>
<version>${version}</version>
</dependency>
可是这些到底哪个才是真正用得上的呢?绕的云里雾里的,头晕目眩。
于是就想找java的示例代码 终于在链接: https://www.huaweicloud.com/product/obs.html?utm_source=3.baidu.com&utm_medium=organic&utm_adplace=kapian
这个页面,点击“文档”这个按钮,进入了产品相关的文档页面。
链接地址: https://support.huaweicloud.com/obs/index.html
这里面左边列表中,找到SDK再找到JAVA选项,就能看到与之相关的JAVA接口使用文档了
在“下载与安装SDK” 这部分,可以看到,官方推荐的配置如下
<dependency>
<groupId>com.huaweicloud</groupId>
<artifactId>esdk-obs-java-bundle</artifactId>
<version>3.23.9</version>
</dependency>
我也就按官方的方法引入了,可还是有问题。咱接着往下看
- 没找到示例代码中的ObsClient类
我按照官方的文档说明引入了之后,照着“快速入门(JAVA SDK)”链接里面的方法去写代码,却发现一个问题
// 创建ObsClient实例
// 使用永久AK/SK初始化客户端
ObsClient obsClient = new ObsClient(ak, sk,endPoint);
文档中ObsClient是三个参数,而我这边用的ObsClient却只能传入对象,而不是这种字符型的参数。
后来也上网找了资料,最后仔细一看才发现这个ObsClient来自于com.huaweicloud.sdk:huaweicloud-sdk-obs:3.1.120 包路径为:com.huaweicloud.sdk.obs.v1
而示例中的包路径是:com.obs.services.ObsClient
参考链接:https://support.huaweicloud.com/sdk-java-devg-obs/obs_21_0100.html
于是按照官方的说明,最终我引入了这些依赖
<dependency>
<groupId>com.huaweicloud</groupId>
<artifactId>esdk-obs-java</artifactId>
<version>3.23.9</version>
</dependency>
<dependency>
<groupId>com.huaweicloud</groupId>
<artifactId>esdk-obs-java-bundle</artifactId>
<version>[3.21.8,)</version>
</dependency>
可还是找不到 com.obs.services.ObsClient 。
后面看文档,看到可以自行编译源码,于是就使用maven命令进行源码编译
mvn clean package "-Dmaven.test.skip=true" -f pom-java.xml
我这里是windows系统,用的是windows的方法
参考链接:https://support.huaweicloud.com/sdk-java-devg-obs/obs_21_0001.html#obs_21_0001__section148723251129
最终如愿以偿,找到了这个包 com.obs.services.ObsClient
- okhttp类库的冲突问题
但进展并没有想象中的顺利,当我集成到系统中的时候,提示okhttp3找不到类文件。于是又找相关的资料解决这个问题。
为了更好的去继承,我单独弄了个测试项目来测试如何才能顺利的把文件传到服务器,于是在测试服务器配置弄好后,在测试过程中提示如下信息:
okhttp3 java.lang.ClassNotFoundException: okhttp3.Protocol
我在示例中,使用了如下依赖,解决了这个问题
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.0</version>
</dependency>
到此,至少我的示例代码是能顺利传文件到华为云Obs了。
直接途径
我的示例代码中,引入了华为云的Demo代码,代码下载地址: https://obssdk.obs.cn-north-1.myhuaweicloud.com/sample/java/PostObjectSample.zip
代码内容如下:
/**
* Copyright 2019 Huawei Technologies Co.,Ltd.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of the
* License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package samples_java;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import com.obs.services.ObsClient;
import com.obs.services.ObsConfiguration;
import com.obs.services.exception.ObsException;
import com.obs.services.model.AuthTypeEnum;
import com.obs.services.model.PostSignatureRequest;
import com.obs.services.model.PostSignatureResponse;
/**
* This sample demonstrates how to post object under specified bucket from
* OBS using the OBS SDK for Java.
*/
public class PostObjectSample
{
private static final String protocol = "https://";
/*
* Example:obs.cn-north-1.myhuaweicloud.com
*/
private static final String endPoint = "your-endpoint";
private static final String ak = "*** Provide your Access Key ***";
private static final String sk = "*** Provide your Secret Key ***";
private static ObsClient obsClient;
private static String bucketName = "my-obs-bucket-demo";
private static String objectKey = "my-obs-object-key-demo";
private static AuthTypeEnum authType = AuthTypeEnum.OBS;
public static void main(String[] args)
throws IOException
{
ObsConfiguration config = new ObsConfiguration();
config.setEndPoint(endPoint);
config.setAuthType(authType);
try
{
/*
* Constructs a obs client instance with your account for accessing OBS
*/
obsClient = new ObsClient(ak, sk, config);
/*
* Create bucket
*/
System.out.println("Create a new bucket for demo\n");
obsClient.createBucket(bucketName);
/*
* Create sample file
*/
File sampleFile = createSampleFile();
/*
* Claim a post object request
*/
PostSignatureRequest request = new PostSignatureRequest();
request.setExpires(3600);
Map<String, Object> formParams = new HashMap<String, Object>();
String contentType = "text/plain";
if(authType == AuthTypeEnum.OBS) {
formParams.put("x-obs-acl", "public-read");
}else {
formParams.put("acl", "public-read");
}
formParams.put("content-type", contentType);
request.setFormParams(formParams);
PostSignatureResponse response = obsClient.createPostSignature(request);
formParams.put("key", objectKey);
formParams.put("policy", response.getPolicy());
if(authType == AuthTypeEnum.OBS) {
formParams.put("signature", response.getSignature());
formParams.put("accesskeyid", ak);
}else {
formParams.put("signature", response.getSignature());
formParams.put("AwsAccesskeyid", ak);
}
String postUrl = protocol + bucketName + "." + endPoint;
System.out.println("Creating object in browser-based way");
System.out.println("\tpost url:" + postUrl);
String res = formUpload(postUrl, formParams, sampleFile, contentType);
System.out.println("\tresponse:"+ res);
}
catch (Exception ex)
{
if (ex instanceof ObsException)
{
ObsException e = (ObsException)ex;
System.out.println("Response Code: " + e.getResponseCode());
System.out.println("Error Message: " + e.getErrorMessage());
System.out.println("Error Code: " + e.getErrorCode());
System.out.println("Request ID: " + e.getErrorRequestId());
System.out.println("Host ID: " + e.getErrorHostId());
}
else
{
ex.printStackTrace();
}
}
finally
{
if (obsClient != null)
{
try
{
/*
* Close obs client
*/
obsClient.close();
}
catch (IOException e)
{
}
}
}
}
private static String formUpload(String postUrl, Map<String, Object> formFields, File sampleFile, String contentType)
{
String res = "";
HttpURLConnection conn = null;
String boundary = "9431149156168";
BufferedReader reader = null;
DataInputStream in = null;
OutputStream out = null;
try
{
URL url = new URL(postUrl);
conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(5000);
conn.setReadTimeout(30000);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("User-Agent", "OBS/Test");
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
out = new DataOutputStream(conn.getOutputStream());
// text
if (formFields != null)
{
StringBuffer strBuf = new StringBuffer();
Iterator<Entry<String, Object>> iter = formFields.entrySet().iterator();
int i = 0;
while (iter.hasNext())
{
Entry<String, Object> entry = iter.next();
String inputName = entry.getKey();
Object inputValue = entry.getValue();
if (inputValue == null)
{
continue;
}
if (i == 0)
{
strBuf.append("--").append(boundary).append("\r\n");
strBuf.append("Content-Disposition: form-data; name=\"" + inputName + "\"\r\n\r\n");
strBuf.append(inputValue);
}
else
{
strBuf.append("\r\n").append("--").append(boundary).append("\r\n");
strBuf.append("Content-Disposition: form-data; name=\"" + inputName + "\"\r\n\r\n");
strBuf.append(inputValue);
}
i++;
}
out.write(strBuf.toString().getBytes());
}
// file
String filename = sampleFile.getName();
if (contentType == null || contentType.equals(""))
{
contentType = "application/octet-stream";
}
StringBuffer strBuf = new StringBuffer();
strBuf.append("\r\n").append("--").append(boundary).append("\r\n");
strBuf.append("Content-Disposition: form-data; name=\"file\"; " + "filename=\"" + filename + "\"\r\n");
strBuf.append("Content-Type: " + contentType + "\r\n\r\n");
out.write(strBuf.toString().getBytes());
in = new DataInputStream(new FileInputStream(sampleFile));
int bytes = 0;
byte[] bufferOut = new byte[1024];
while ((bytes = in.read(bufferOut)) != -1)
{
out.write(bufferOut, 0, bytes);
}
byte[] endData = ("\r\n--" + boundary + "--\r\n").getBytes();
out.write(endData);
out.flush();
// 读取返回数据
strBuf = new StringBuffer();
reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null)
{
strBuf.append(line).append("\n");
}
res = strBuf.toString();
}
catch (Exception e)
{
System.out.println("Send post request exception: " + e);
e.printStackTrace();
}
finally
{
if(out != null){
try
{
out.close();
}
catch (IOException e)
{
}
}
if(in != null){
try
{
in.close();
}
catch (IOException e)
{
}
}
if(reader != null){
try
{
reader.close();
}
catch (IOException e)
{
}
}
if (conn != null)
{
conn.disconnect();
conn = null;
}
}
return res;
}
private static File createSampleFile()
throws IOException
{
File file = File.createTempFile("obs-java-sdk-", ".txt");
file.deleteOnExit();
Writer writer = new OutputStreamWriter(new FileOutputStream(file));
writer.write("abcdefghijklmnopqrstuvwxyz\n");
writer.write("0123456789011234567890\n");
writer.close();
return file;
}
}
总结
当初是由于阿里云到期了,想要换成华为云,于是就开始了这个工作,可是前面由于比较紧急,也没有理清思路,就到处找示例程序啥的,也没专心去研究,所以一连弄了几天,也没有接入进来,这两天事情少点了,冷静下来想想,还是先做个单元测试,在示例代码中能顺利的话,接入到系统中障碍也会更少些。
也提醒各位,编码之前尽量想清楚怎么做,再去动手,会比较节约时间成本。
最近观察微信公众号发文已经到了30+,阅读量峰最高到了240+,技术文章阅读量最高也有50+ 点赞转发虽然不多,但也总比没有的好。从数据可以看出咱们的文章质量还是挺高的,这些成绩就是我们前进的动力! 欢迎大家多多关注和支持! 我们一直会保持不定期更新,绝对不会放弃,让我们都遇到更好的自己!