OSS在完成文件(Object)上传时可以提供回调(Callback)给应用服务器。您只需要在发送给OSS的请求中携带相应的Callback参数,即可实现上传回调。
注意事项
本文以华东1(杭州)外网Endpoint为例。如果您希望通过与OSS同地域的其他阿里云产品访问OSS,请使用内网Endpoint。关于OSS支持的Region与Endpoint的对应关系,请参见OSS地域和访问域名。
本文以从环境变量读取访问凭证为例。如何配置访问凭证,请参见Java配置访问凭证。
本文以OSS域名新建OSSClient为例。如果您希望通过自定义域名、STS等方式新建OSSClient,请参见常见场景配置示例。
示例代码
简单上传回调
import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.Callback;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import java.io.ByteArrayInputStream;
public class Demo {
public static void main(String[] args) throws Exception{
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
// 填写Bucket名称,例如examplebucket。
String bucketName = "examplebucket";
// 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。
String objectName = "exampledir/exampleobject.txt";
// 您的回调服务器地址,例如https://example.com:23450。
String callbackUrl = "yourCallbackServerUrl";
// 填写Bucket所在地域。以华东1(杭州)为例,Region填写为cn-hangzhou。
String region = "cn-hangzhou";
// 创建 OSSClient 实例,配置 V4 签名版本。
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
try {
String content = "Hello OSS";
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName,new ByteArrayInputStream(content.getBytes()));
// 配置上传回调参数
Callback callback = new Callback();
callback.setCallbackUrl(callbackUrl);
//(可选)设置回调请求消息头中Host的值,即您的服务器配置Host的值。
// callback.setCallbackHost("yourCallbackHost");
// 设置回调请求的 Body 内容,采用 JSON 格式,并在其中定义占位符变量。
callback.setCalbackBodyType(Callback.CalbackBodyType.JSON);
callback.setCallbackBody("{\\\"bucket\\\":${bucket},\\\"object\\\":${object},\\\"mimeType\\\":${mimeType},\\\"size\\\":${size},\\\"my_var1\\\":${x:var1},\\\"my_var2\\\":${x:var2}}");
// 设置发起回调请求的自定义参数,由Key和Value组成,Key必须以x:开始。
callback.addCallbackVar("x:var1", "value1");
callback.addCallbackVar("x:var2", "value2");
putObjectRequest.setCallback(callback);
// 执行上传操作
PutObjectResult putObjectResult = ossClient.putObject(putObjectRequest);
// 读取上传回调返回的消息内容。
byte[] buffer = new byte[1024];
putObjectResult.getResponse().getContent().read(buffer);
// 数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作。
putObjectResult.getResponse().getContent().close();
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (Throwable ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
}
分片上传回调
import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.internal.Mimetypes;
import com.aliyun.oss.model.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) throws Exception {
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
// 填写Bucket名称,例如examplebucket。
String bucketName = "examplebucket";
// 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。
String objectName = "exampledir/exampleobject.txt";
// 待上传本地文件路径。
String filePath = "D:\\localpath\\examplefile.txt";
// 填写Bucket所在地域。以华东1(杭州)为例,Region填写为cn-hangzhou。
String region = "cn-hangzhou";
// 您的回调服务器地址,例如https://example.com:23450。
String callbackUrl = "https://example.com:23450";
// 创建 OSSClient 实例,配置 V4 签名版本。
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
try {
// 创建InitiateMultipartUploadRequest对象。
InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, objectName);
// 设置Object的元数据,并获取文件的Content-Type。
ObjectMetadata metadata = new ObjectMetadata();
if (metadata.getContentType() == null) {
metadata.setContentType(Mimetypes.getInstance().getMimetype(new File(filePath), objectName));
}
System.out.println("Content-Type: " + metadata.getContentType());
// 将metadata绑定到上传请求中。
request.setObjectMetadata(metadata);
// 初始化分片。
InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);
// 返回uploadId。
String uploadId = upresult.getUploadId();
// partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。
List<PartETag> partETags = new ArrayList<PartETag>();
// 每个分片的大小,用于计算文件有多少个分片。单位为字节。
// 分片最小值为100 KB,最大值为5 GB。最后一个分片的大小允许小于100 KB。
// 设置分片大小为 1 MB。
final long partSize = 1 * 1024 * 1024L;
// 根据上传的数据大小计算分片数。以本地文件为例,说明如何通过File.length()获取上传数据的大小。
final File sampleFile = new File(filePath);
long fileLength = sampleFile.length();
int partCount = (int) (fileLength / partSize);
if (fileLength % partSize != 0) {
partCount++;
}
// 遍历分片上传。
for (int i = 0; i < partCount; i++) {
long startPos = i * partSize;
long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
UploadPartRequest uploadPartRequest = new UploadPartRequest();
uploadPartRequest.setBucketName(bucketName);
uploadPartRequest.setKey(objectName);
uploadPartRequest.setUploadId(uploadId);
// 设置上传的分片流。
// 以本地文件为例说明如何创建FileInputStream,并通过InputStream.skip()方法跳过指定数据。
InputStream instream = new FileInputStream(sampleFile);
instream.skip(startPos);
uploadPartRequest.setInputStream(instream);
// 设置分片大小。
uploadPartRequest.setPartSize(curPartSize);
// 设置分片号。每一个上传的分片都有一个分片号,取值范围是1~10000,如果超出此范围,OSS将返回InvalidArgument错误码。
uploadPartRequest.setPartNumber(i + 1);
// 每个分片不需要按顺序上传,甚至可以在不同客户端上传,OSS会按照分片号排序组成完整的文件。
UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
// 每次上传分片之后,OSS的返回结果包含PartETag。PartETag将被保存在partETags中。
partETags.add(uploadPartResult.getPartETag());
// 关闭流
instream.close();
}
// 创建CompleteMultipartUploadRequest对象。
// 在执行完成分片上传操作时,需要提供所有有效的partETags。OSS收到提交的partETags后,会逐一验证每个分片的有效性。当所有的数据分片验证通过后,OSS将把这些分片组合成一个完整的文件。
CompleteMultipartUploadRequest completeMultipartUploadRequest =
new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);
// 设置上传回调参数。
Callback callback = new Callback();
callback.setCallbackUrl(callbackUrl);
//(可选)设置回调请求消息头中Host的值,即您的服务器配置Host的值。
// callback.setCallbackHost("yourCallbackHost");
// 设置回调请求的Body内容,采用JSON格式,并定义回调Body的占位符变量。
callback.setCalbackBodyType(Callback.CalbackBodyType.JSON);
callback.setCallbackBody("{\\\"bucket\\\":${bucket},\\\"object\\\":${object},\\\"mimeType\\\":${mimeType},\\\"size\\\":${size},\\\"my_var1\\\":${x:var1},\\\"my_var2\\\":${x:var2}}");
// 设置发起回调请求的自定义参数,由Key和Value组成,Key必须以x:开始。
callback.addCallbackVar("x:var1", "value1");
callback.addCallbackVar("x:var2", "value2");
completeMultipartUploadRequest.setCallback(callback);
// 完成分片上传。
CompleteMultipartUploadResult completeMultipartUploadResult = ossClient.completeMultipartUpload(completeMultipartUploadRequest);
System.out.println("上传成功,ETag:" + completeMultipartUploadResult.getETag());
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught a ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
}
表单上传回调
import com.aliyun.oss.ClientException;
import com.aliyun.oss.common.auth.ServiceSignature;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.common.utils.StringUtils;
import com.aliyun.oss.internal.OSSUtils;
import com.aliyun.oss.model.Callback;
import org.apache.commons.codec.binary.Base64;
import javax.activation.MimetypesFileTypeMap;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
public class PostObjectCallbackV4Demo {
// 填写待上传的本地文件的完整路径。
private static final String localFilePath = "D:\\localpath\\examplefile.txt";
// Endpoint以杭州为例,其它Region请按实际情况填写。
private static final String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
// 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录RAM控制台创建RAM账号。
private static final String accessKeyId = System.getenv("OSS_ACCESS_KEY_ID");
private static final String accessKeySecret = System.getenv("OSS_ACCESS_KEY_SECRET");
// 填写Bucket名称,例如examplebucket。
private static final String bucketName = "examplebucket";
// 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。
private static final String objectName = "exampledir/exampleobject.txt";
// 设置回调服务器地址,例如http://oss-demo.oss-cn-hangzhou.aliyuncs.com:23450或http://127.0.0.1:9090。
private static final String callbackServerUrl = "http://oss-demo.oss-cn-hangzhou.aliyuncs.com:23450";
// 设置回调请求消息头中Host的值,例如oss-cn-hangzhou.aliyuncs.com。
private static final String callbackServerHost = "";
// 填写Bucket所在地域。以华东1(杭州)为例,Region填写为cn-hangzhou。
private static final String region = "cn-hangzhou";
private static Date requestDateTime = new Date();
/**
* 表单上传。
* @throws Exception
*/
private void PostObject() throws Exception {
// 在URL中添加Bucket名称,添加后URL格式为http://yourBucketName.oss-cn-hangzhou.aliyuncs.com。
String urlStr = endpoint.replace("http://", "http://" + bucketName+ ".");
// 构造表单参数
Map<String, String> formFields = new LinkedHashMap<String, String>();
formFields.put("key", objectName);
formFields.put("Content-Disposition", "attachment;filename="
+ localFilePath);
// 设置回调参数。
Callback callback = new Callback();
// 设置回调服务器地址,例如http://oss-demo.oss-cn-hangzhou.aliyuncs.com:23450或http://127.0.0.1:9090。
callback.setCallbackUrl(callbackServerUrl);
// 设置回调请求消息头中Host的值,如oss-cn-hangzhou.aliyuncs.com。
callback.setCallbackHost(callbackServerHost);
// 设置回调请求的Body内容,采用JSON格式,并定义回调Body的占位符变量。
callback.setCalbackBodyType(Callback.CalbackBodyType.JSON);
callback.setCallbackBody("{\\\"bucket\\\":${bucket},\\\"object\\\":${object},\\\"mimeType\\\":${mimeType},\\\"size\\\":${size},\\\"my_var1\\\":${x:var1},\\\"my_var2\\\":${x:var2}}");
// 设置发起回调请求的自定义参数,由Key和Value组成,Key必须以x:开始,且必须小写。
callback.addCallbackVar("x:var1", "value1");
callback.addCallbackVar("x:var2", "value2");
// 在表单Map中设置回调参数。
setCallBack(formFields, callback);
// 设置OSSAccessKeyId。
formFields.put("OSSAccessKeyId", accessKeyId);
String policy = "{\"expiration\": \"2120-01-01T12:00:00.000Z\",\"conditions\": [" +
" {\"x-oss-signature-version\": \"OSS4-HMAC-SHA256\"},\n" +
" {\"x-oss-credential\": \""+accessKeyId+"/"+getDate()+"/"+region+"/oss/aliyun_v4_request\"},\n" +
" {\"x-oss-date\": \""+getDateTime()+"\"},\n" +
" [\"content-length-range\", 0, 104857600]" +
"]}";
String encodePolicy = new String(Base64.encodeBase64(policy.getBytes()));
// 设置policy。
formFields.put("policy", encodePolicy);
System.out.println("policy:" + policy);
// 生成签名。
formFields.put("x-oss-signature-version", "OSS4-HMAC-SHA256");
formFields.put("x-oss-credential", accessKeyId+"/"+getDate()+"/"+region+"/oss/aliyun_v4_request");
formFields.put("x-oss-date", getDateTime());
String stringToSign = new String(Base64.encodeBase64(policy.getBytes()));
System.out.println("stringToSign:" + stringToSign);
byte[] signingKey = buildSigningKey();
String signature = buildSignature(signingKey, stringToSign);
formFields.put("x-oss-signature", signature);
System.out.println("Signature:" + signature);
// 执行上传
String ret = formUpload(urlStr, formFields);
System.out.println("Post Object [" + objectName + "] to bucket [" + bucketName + "]");
System.out.println("post reponse:" + ret);
}
private static String formUpload(String urlStr, Map<String, String> formFields)
throws Exception {
String res = "";
HttpURLConnection conn = null;
String boundary = "9431149156168";
try {
URL url = new URL(urlStr);
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setReadTimeout(30000);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("User-Agent",
"Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)");
conn.setRequestProperty("Content-Type",
"multipart/form-data; boundary=" + boundary);
// 处理表单字段
OutputStream out = new DataOutputStream(conn.getOutputStream());
// 遍历读取表单Map中的数据,将数据写入到输出流中。
if (formFields != null) {
StringBuilder strBuf = new StringBuilder();
Iterator<Map.Entry<String, String>> iter = formFields.entrySet().iterator();
int i = 0;
while (iter.hasNext()) {
Map.Entry<String, String> entry = iter.next();
String inputName = entry.getKey();
String inputValue = entry.getValue();
if (inputValue == null) {
continue;
}
if (i == 0) {
strBuf.append("--").append(boundary).append("\r\n");
strBuf.append("Content-Disposition: form-data; name=\"").append(inputName).append("\"\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 file = new File(localFilePath);
String filename = file.getName();
String contentType = new MimetypesFileTypeMap().getContentType(file);
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());
DataInputStream in = new DataInputStream(new FileInputStream(file));
int bytes = 0;
byte[] bufferOut = new byte[1024];
while ((bytes = in.read(bufferOut)) != -1) {
out.write(bufferOut, 0, bytes);
}
in.close();
byte[] endData = ("\r\n--" + boundary + "--\r\n").getBytes();
out.write(endData);
out.flush();
out.close();
// 读取返回数据。
strBuf = new StringBuffer();
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
strBuf.append(line).append("\n");
}
res = strBuf.toString();
reader.close();
reader = null;
} catch (ClientException e) {
System.err.println("Send post request exception: " + e);
System.err.println(e.getErrorCode()+" msg="+e.getMessage());
throw e;
} finally {
if (conn != null) {
conn.disconnect();
conn = null;
}
}
return res;
}
private static void setCallBack(Map<String, String> formFields, Callback callback) {
if (callback != null) {
String jsonCb = OSSUtils.jsonizeCallback(callback);
String base64Cb = BinaryUtil.toBase64String(jsonCb.getBytes());
formFields.put("callback", base64Cb);
if (callback.hasCallbackVar()) {
Map<String, String> varMap = callback.getCallbackVar();
for (Map.Entry<String, String> entry : varMap.entrySet()) {
formFields.put(entry.getKey(), entry.getValue());
}
}
}
}
private static String getDateTime() {
return getIso8601DateTimeFormat().format(requestDateTime);
}
private static DateFormat getIso8601DateTimeFormat() {
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.US);
df.setTimeZone(new SimpleTimeZone(0, "GMT"));
return df;
}
private static DateFormat getIso8601DateFormat() {
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd", Locale.US);
df.setTimeZone(new SimpleTimeZone(0, "GMT"));
return df;
}
private static String getDate() {
return getIso8601DateFormat().format(requestDateTime);
}
private String buildSignature(byte[] signingKey, String stringToSign) {
byte[] result = ServiceSignature.create("HmacSHA256").computeHash(signingKey, stringToSign.getBytes(StringUtils.UTF8));
return BinaryUtil.toHex(result);
}
private byte[] buildSigningKey() {
ServiceSignature signature = ServiceSignature.create("HmacSHA256");
byte[] signingSecret = ("aliyun_v4" + accessKeySecret).getBytes(StringUtils.UTF8);
byte[] signingDate = signature.computeHash(signingSecret, getDate().getBytes(StringUtils.UTF8));
byte[] signingRegion = signature.computeHash(signingDate, region.getBytes(StringUtils.UTF8));
byte[] signingService = signature.computeHash(signingRegion, "oss".getBytes(StringUtils.UTF8));
/*System.out.println("signingSecret:\n" + BinaryUtil.toHex(signingSecret));
System.out.println("signingDate:\n" + BinaryUtil.toHex(signingDate));
System.out.println("signingRegion:\n" + BinaryUtil.toHex(signingRegion));
System.out.println("signingService:\n" + BinaryUtil.toHex(signingService));*/
return signature.computeHash(signingService, "aliyun_v4_request".getBytes(StringUtils.UTF8));
}
public static void main(String[] args) throws Exception {
PostObjectCallbackV4Demo ossPostObject = new PostObjectCallbackV4Demo();
ossPostObject.PostObject();
}
}