上传回调是指客户端在请求时携带回调(Callback)参数,服务端在上传完成后,发送同步的 POST 回调请求到 CallBack 中指定的第三方应用服务器,在服务器确认接受并返回结果后,才将所有结果返回给客户端。
关于上传回调的详细介绍,请参见上传回调。
从 2.6.0 版本开始,Java SDK 支持在 putObject 和 completeMultipartUpload 接口设置上传回调参数。
import com.volcengine.tos.TOSV2; import com.volcengine.tos.TOSV2ClientBuilder; import com.volcengine.tos.TosClientException; import com.volcengine.tos.TosServerException; import com.volcengine.tos.model.object.PutObjectInput; import com.volcengine.tos.model.object.PutObjectOutput; import java.io.ByteArrayInputStream; public class PutObjectWithCallbackExample { public static void main(String[] args) { String endpoint = "your endpoint"; String region = "your region"; String accessKey = System.getenv("TOS_ACCESS_KEY"); String secretKey = System.getenv("TOS_SECRET_KEY"); String bucketName = "bucket-example"; String objectKey = "example_dir/example_object.txt"; // 上传回调参数 String callback = "your callback param"; // 上传回调自定义变量 String callbackVar = "your callback custom variable"; TOSV2 tos = new TOSV2ClientBuilder().build(region, endpoint, accessKey, secretKey); try{ // 上传的数据内容,以 String 的形式 String data = "1234567890abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+<>?,./ :'1234567890abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+<>?,./ :'"; // 也可以以 byte 数组的形式上传 byte[] dataInBytes = data.getBytes(); // 统一封装成 ByteArrayInputStream ByteArrayInputStream stream = new ByteArrayInputStream(dataInBytes); PutObjectInput putObjectInput = new PutObjectInput() .setBucket(bucketName).setKey(objectKey).setContent(stream) .setCallback(Base64.getEncoder().encodeToString(callback.getBytes(StandardCharsets.UTF_8))) .setCallbackVar(Base64.getEncoder().encodeToString(callbackVar.getBytes(StandardCharsets.UTF_8))); PutObjectOutput output = tos.putObject(putObjectInput); System.out.println("putObject succeed, object's etag is " + output.getEtag()); System.out.println("putObject succeed, object's crc64 is " + output.getHashCrc64ecma()); } catch (TosClientException e) { // 操作失败,捕获客户端异常,一般情况是请求参数错误,此时请求并未发送 System.out.println("putObject failed"); System.out.println("Message: " + e.getMessage()); if (e.getCause() != null) { e.getCause().printStackTrace(); } } catch (TosServerException e) { // 操作失败,捕获服务端异常,可以获取到从服务端返回的详细错误信息 System.out.println("putObject failed"); System.out.println("StatusCode: " + e.getStatusCode()); System.out.println("Code: " + e.getCode()); System.out.println("Message: " + e.getMessage()); System.out.println("RequestID: " + e.getRequestID()); } catch (Throwable t) { // 作为兜底捕获其他异常,一般不会执行到这里 System.out.println("putObject failed"); System.out.println("unexpected exception, message: " + t.getMessage()); } } }
import com.volcengine.tos.TOSV2; import com.volcengine.tos.TOSV2ClientBuilder; import com.volcengine.tos.TosClientException; import com.volcengine.tos.TosServerException; import com.volcengine.tos.model.object.CompleteMultipartUploadV2Input; import com.volcengine.tos.model.object.CompleteMultipartUploadV2Output; import com.volcengine.tos.model.object.UploadedPartV2; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Base64; import java.util.List; public class CompleteMultipartWithCallbackExample { public static void main(String[] args) { String endpoint = "your endpoint"; String region = "your region"; String accessKey = System.getenv("TOS_ACCESS_KEY"); String secretKey = System.getenv("TOS_SECRET_KEY"); String bucketName = "bucket-example"; String objectKey = "example_dir/example_object.txt"; // 指定的需要合并的分片上传任务的uploadID, // 需保证该 uploadId 已通过初始化分片上传接口 createMultipartUpload 调用返回。 // 否则,对于不存在的 uploadId 会返回 404 not found。 String uploadId = "the specific uploadId"; // 上传回调参数 String callback = "your callback param"; // 上传回调自定义变量 String callbackVar = "your callback custom variable"; // 已上传分片列表 List<UploadedPartV2> uploadedParts = new ArrayList<>(); TOSV2 tos = new TOSV2ClientBuilder().build(region, endpoint, accessKey, secretKey); try { CompleteMultipartUploadV2Input complete = new CompleteMultipartUploadV2Input().setBucket(bucketName) .setKey(objectKey).setUploadID(uploadId) .setCallback(Base64.getEncoder().encodeToString(callback.getBytes(StandardCharsets.UTF_8))) .setCallbackVar(Base64.getEncoder().encodeToString(callbackVar.getBytes(StandardCharsets.UTF_8))); complete.setUploadedParts(uploadedParts); CompleteMultipartUploadV2Output completedOutput = tos.completeMultipartUpload(complete); System.out.printf("completeMultipartUpload succeed, etag is %s, crc64 value is %s, location is %s.\n", completedOutput.getEtag(), completedOutput.getHashCrc64ecma(), completedOutput.getLocation()); } catch (TosClientException e) { // 操作失败,捕获客户端异常,一般情况是请求参数错误,此时请求并未发送 System.out.println("putObject failed"); System.out.println("Message: " + e.getMessage()); if (e.getCause() != null) { e.getCause().printStackTrace(); } } catch (TosServerException e) { // 操作失败,捕获服务端异常,可以获取到从服务端返回的详细错误信息 System.out.println("putObject failed"); System.out.println("StatusCode: " + e.getStatusCode()); System.out.println("Code: " + e.getCode()); System.out.println("Message: " + e.getMessage()); System.out.println("RequestID: " + e.getRequestID()); } catch (Throwable t) { // 作为兜底捕获其他异常,一般不会执行到这里 System.out.println("putObject failed"); System.out.println("unexpected exception, message: " + t.getMessage()); } } }
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.security.*; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Base64; import java.util.Map; import java.util.TreeMap; public class CallbackHandler { private static final String AUTHORIZATION = "Authorization"; private static final String XTOS_PUBLIC_KEY_URL = "x-tos-pub-key-url"; public void callbackHandler(HttpServletRequest req, HttpServletResponse resp) { byte[] sign = getSignature(req); if (sign == null) { System.out.println("get callback signature error"); resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } byte[] publicKey = getPublicKey(req); if (publicKey == null) { resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } byte[] bodyContent; byte[] signMd5; try { Object[] contentAndSignMD5 = getContentAndSignMD5(req); signMd5 = (byte[]) contentAndSignMD5[0]; bodyContent = (byte[]) contentAndSignMD5[1]; } catch (IOException | NoSuchAlgorithmException e) { resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } try { verifySignature(publicKey, signMd5, sign); System.out.println(new String(bodyContent)); resp.setStatus(HttpServletResponse.SC_OK); resp.getWriter().write("{\"msg\":\"ok\"}"); } catch (Exception e) { resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); } } public byte[] getSignature(HttpServletRequest req) { String authorization = req.getHeader(AUTHORIZATION); if (authorization == null || authorization.isEmpty()) { return null; } return Base64.getDecoder().decode(authorization); } public byte[] getPublicKey(HttpServletRequest req) { byte[] bytePublicKey = null; String publicKeyURLBase64 = req.getHeader(XTOS_PUBLIC_KEY_URL); if (publicKeyURLBase64 == null || publicKeyURLBase64.isEmpty()) { return bytePublicKey; } byte[] publicKeyURLBytes = Base64.getDecoder().decode(publicKeyURLBase64); try { URL url = new URL(new String(publicKeyURLBytes)); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); if (connection.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST) { throw new IOException("Get public key failed"); } ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int nRead; byte[] data = new byte[16384]; while ((nRead = connection.getInputStream().read(data, 0, data.length)) != -1) { buffer.write(data, 0, nRead); } buffer.flush(); bytePublicKey = buffer.toByteArray(); connection.disconnect(); } catch (IOException e) { e.printStackTrace(); } return bytePublicKey; } public Object[] getContentAndSignMD5(HttpServletRequest req) throws IOException, NoSuchAlgorithmException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); byte[] buff = new byte[16384]; int nRead; while ((nRead = req.getInputStream().read(buff, 0, buff.length)) != -1) { buffer.write(buff, 0, nRead); } buffer.flush(); byte[] bodyContent = buffer.toByteArray(); String stringToSign = calcStringToSign(req, bodyContent); MessageDigest md = MessageDigest.getInstance("MD5"); byte[] signMd5 = md.digest(stringToSign.getBytes()); return new Object[]{signMd5, bodyContent}; } public String calcStringToSign(HttpServletRequest req, byte[] body) { StringBuilder buf = new StringBuilder(); buf.append(req.getRequestURI()); Map<String, String[]> query = req.getParameterMap(); if (!query.isEmpty()) { buf.append('?'); Map<String, String> sorted = new TreeMap<>(); for (Map.Entry<String, String[]> entry : query.entrySet()) { sorted.put(entry.getKey(), entry.getValue()[0]); } boolean first = true; for (Map.Entry<String, String> entry : sorted.entrySet()) { if (!first) { buf.append('&'); } buf.append(entry.getKey()); buf.append('='); buf.append(entry.getValue()); first = false; } } buf.append('\n'); buf.append(new String(body)); return buf.toString(); } public void verifySignature(byte[] publicKey, byte[] signMd5, byte[] sign) throws Exception { CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(publicKey)); PublicKey pub = cert.getPublicKey(); Signature signature = Signature.getInstance("MD5withRSA"); signature.initVerify(pub); signature.update(signMd5); if (!signature.verify(sign)) { throw new SignatureException("Invalid signature"); } } }