You need to enable JavaScript to run this app.
文档中心
文档控制台
注册
对象存储

对象存储

复制全文
下载 pdf
上传对象
上传回调(Java SDK)
复制全文
下载 pdf
上传回调(Java SDK)

上传回调是指客户端在请求时携带回调(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.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

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;
        }

        String stringToSign;
        try {
            stringToSign = getStringToSign(req);
        } catch (IOException | NoSuchAlgorithmException e) {
            resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }

        try {
            verifySignature(publicKey, stringToSign, sign);
            resp.setStatus(HttpServletResponse.SC_OK);
            resp.getWriter().write("{\"Status\":\"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 String getStringToSign(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);
        System.out.println(stringToSign);
        System.out.println(new String(bodyContent, StandardCharsets.UTF_8));
        return stringToSign;
    }

    public String calcStringToSign(HttpServletRequest req, byte[] body) {
        StringBuilder buf = new StringBuilder();
        // 处理路径:使用已解码的路径(url_decode(path))
        String path = req.getRequestURI();
        buf.append(path);

        // 处理查询参数:获取原始查询字符串并解析
        String queryString = req.getQueryString();
        Map<String, List<String>> sortedParams = new TreeMap<>();
        if (queryString != null && !queryString.isEmpty()) {
            String[] pairs = queryString.split("&");
            for (String pair : pairs) {
                int idx = pair.indexOf("=");
                String key = (idx > 0) ? pair.substring(0, idx) : pair;
                String value = (idx > 0 && pair.length() > idx + 1) ? pair.substring(idx + 1) : "";
                // URL解码键和值
                try {
                    key = URLDecoder.decode(key, StandardCharsets.UTF_8.name());
                    value = URLDecoder.decode(value, StandardCharsets.UTF_8.name());
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e);
                }
                sortedParams.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
            }
        }

        // 拼接排序后的查询参数
        if (!sortedParams.isEmpty()) {
            buf.append('?');
            boolean firstKey = true;
            for (Map.Entry<String, List<String>> entry : sortedParams.entrySet()) {
                String key = entry.getKey();
                List<String> values = entry.getValue();
                // 对每个值进行排序(根据需求决定是否需要)
                Collections.sort(values);
                for (String value : values) {
                    if (!firstKey) {
                        buf.append('&');
                    }
                    buf.append(key).append('=').append(value);
                    firstKey = false;
                }
            }
        }

        buf.append('\n');
        // 使用UTF-8编码转换body
        buf.append(new String(body, StandardCharsets.UTF_8));
        return buf.toString();
    }

    public PublicKey loadPublicKey(byte[] publicKeyPEMBytes) throws Exception {
        String publicKeyPEM = new String(publicKeyPEMBytes, StandardCharsets.UTF_8);

        // 去掉头尾标识
        String publicKeyPEMFormatted = publicKeyPEM
                .replace("-----BEGIN PUBLIC KEY-----", "")
                .replace("-----END PUBLIC KEY-----", "")
                .replaceAll("\\s+", ""); // 去掉换行和空格

        // Base64解码
        byte[] encoded = Base64.getDecoder().decode(publicKeyPEMFormatted);

        // 生成公钥对象
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
        return keyFactory.generatePublic(keySpec);
    }

    public void verifySignature(byte[] publicKey, String stringToSign, byte[] sign) throws Exception {
        PublicKey pubKey = loadPublicKey(publicKey);

        Signature verifier = Signature.getInstance("MD5withRSA");
        verifier.initVerify(pubKey);
        verifier.update(stringToSign.getBytes(StandardCharsets.UTF_8));

        if (!verifier.verify(sign)) {
            throw new SignatureException("Invalid signature");
        }
    }
}
最近更新时间:2025.02.27 19:41:05
这个页面对您有帮助吗?
有用
有用
无用
无用