Skip to content

Commit cee5f88

Browse files
authored
feat(oss): reuse upload certificate (#154)
1 parent 3081ef9 commit cee5f88

File tree

7 files changed

+568
-78
lines changed

7 files changed

+568
-78
lines changed

samples/MultiModalConversationQwenVL.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public String call() {
5252
return currentTime;
5353
}
5454
}
55-
public static void videoImageListSample() throws ApiException, NoApiKeyException, UploadFileException {
55+
public static void imageSample() throws ApiException, NoApiKeyException, UploadFileException {
5656
MultiModalConversation conversation = new MultiModalConversation();
5757
MultiModalMessageItemText systemText = new MultiModalMessageItemText("你是达摩院的生活助手机器人。");
5858

@@ -71,6 +71,28 @@ public static void videoImageListSample() throws ApiException, NoApiKeyException
7171
});
7272
}
7373

74+
public static void videoSample() throws ApiException, NoApiKeyException, UploadFileException {
75+
MultiModalConversation conv = new MultiModalConversation();
76+
MultiModalMessage systemMessage = MultiModalMessage.builder()
77+
.role(Role.SYSTEM.getValue())
78+
.content(Arrays.asList(Collections.singletonMap("text", "You are a helpful assistant.")))
79+
.build();
80+
MultiModalMessage userMessage = MultiModalMessage.builder()
81+
.role(Role.USER.getValue())
82+
.content(Arrays.asList(Collections.singletonMap("video", Arrays.asList(
83+
"/Users/zhiyi/Downloads/vl_data/1.jpg",
84+
"/Users/zhiyi/Downloads/vl_data/2.jpg",
85+
"/Users/zhiyi/Downloads/vl_data/3.jpg",
86+
"/Users/zhiyi/Downloads/vl_data/4.jpg")),
87+
Collections.singletonMap("text", "描述这个视频的具体s过程")))
88+
.build();
89+
MultiModalConversationParam param = MultiModalConversationParam.builder()
90+
.model("qwen-vl-max-latest").message(systemMessage)
91+
.message(userMessage).build();
92+
MultiModalConversationResult result = conv.call(param);
93+
System.out.print(JsonUtils.toJson(result));
94+
}
95+
7496
public static void streamCallWithToolCalls()
7597
throws NoApiKeyException, ApiException, UploadFileException {
7698
SchemaGeneratorConfigBuilder configBuilder =
@@ -133,7 +155,8 @@ public static void streamCallWithToolCalls()
133155

134156
public static void main(String[] args) {
135157
try {
136-
videoImageListSample();
158+
// imageSample();
159+
videoSample();
137160
// streamCallWithToolCalls();
138161
} catch (ApiException | NoApiKeyException | UploadFileException e) {
139162
System.out.println(e.getMessage());

src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.alibaba.dashscope.exception.NoApiKeyException;
99
import com.alibaba.dashscope.exception.UploadFileException;
1010
import com.alibaba.dashscope.protocol.*;
11+
import com.alibaba.dashscope.utils.OSSUploadCertificate;
1112
import com.alibaba.dashscope.utils.ParamUtils;
1213
import com.alibaba.dashscope.utils.PreprocessMessageInput;
1314
import io.reactivex.Flowable;
@@ -225,19 +226,26 @@ public void onError(Exception e) {
225226
private void preprocessInput(MultiModalConversationParam param)
226227
throws NoApiKeyException, UploadFileException {
227228
boolean hasUpload = false;
229+
OSSUploadCertificate certificate = null;
230+
228231
for (Object msg : param.getMessages()) {
229232
boolean isUpload = false;
230233
if (msg instanceof MultiModalConversationMessage) {
231-
isUpload =
234+
PreprocessMessageInput.PreprocessResult result =
232235
PreprocessMessageInput.preProcessMessageInputs(
233236
param.getModel(),
234237
((MultiModalConversationMessage) msg).getContent(),
235-
param.getApiKey());
236-
238+
param.getApiKey(),
239+
certificate);
240+
isUpload = result.hasUpload();
241+
certificate = result.getCertificate();
237242
} else {
238-
isUpload =
243+
PreprocessMessageInput.PreprocessResult result =
239244
PreprocessMessageInput.preProcessMultiModalMessageInputs(
240-
param.getModel(), (MultiModalMessage) msg, param.getApiKey());
245+
param.getModel(), (MultiModalMessage) msg,
246+
param.getApiKey(), certificate);
247+
isUpload = result.hasUpload();
248+
certificate = result.getCertificate();
241249
}
242250
if (isUpload && !hasUpload) {
243251
hasUpload = true;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.alibaba.dashscope.utils;
2+
3+
import lombok.Data;
4+
5+
/**
6+
* OSS upload certificate for reuse across multiple file uploads.
7+
*/
8+
@Data
9+
public class OSSUploadCertificate {
10+
private String uploadHost;
11+
private String ossAccessKeyId;
12+
private String signature;
13+
private String policy;
14+
private String uploadDir;
15+
private String xOssObjectAcl;
16+
private String xOssForbidOverwrite;
17+
18+
/**
19+
* Create certificate from upload info data.
20+
*
21+
* @param uploadHost OSS upload host
22+
* @param ossAccessKeyId OSS access key ID
23+
* @param signature Upload signature
24+
* @param policy Upload policy
25+
* @param uploadDir Upload directory
26+
* @param xOssObjectAcl OSS object ACL
27+
* @param xOssForbidOverwrite OSS forbid overwrite flag
28+
*/
29+
public OSSUploadCertificate(String uploadHost, String ossAccessKeyId,
30+
String signature, String policy, String uploadDir,
31+
String xOssObjectAcl, String xOssForbidOverwrite) {
32+
this.uploadHost = uploadHost;
33+
this.ossAccessKeyId = ossAccessKeyId;
34+
this.signature = signature;
35+
this.policy = policy;
36+
this.uploadDir = uploadDir;
37+
this.xOssObjectAcl = xOssObjectAcl;
38+
this.xOssForbidOverwrite = xOssForbidOverwrite;
39+
}
40+
}
41+

src/main/java/com/alibaba/dashscope/utils/OSSUtils.java

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,65 @@
3333

3434
@Slf4j
3535
public final class OSSUtils {
36+
/**
37+
* Upload file to OSS without certificate reuse.
38+
*
39+
* @param model Model name
40+
* @param filePath Local file path
41+
* @param apiKey API key
42+
* @return OSS URL
43+
* @throws NoApiKeyException If API key is missing
44+
*/
3645
public static String upload(String model, String filePath, String apiKey)
3746
throws NoApiKeyException {
47+
UploadResult result = uploadWithCertificate(model, filePath, apiKey,
48+
null);
49+
return result.getOssUrl();
50+
}
51+
52+
/**
53+
* Upload file to OSS with optional certificate reuse.
54+
*
55+
* @param model Model name
56+
* @param filePath Local file path
57+
* @param apiKey API key
58+
* @param certificate Optional upload certificate for reuse
59+
* @return UploadResult containing OSS URL and certificate
60+
* @throws NoApiKeyException If API key is missing
61+
*/
62+
public static UploadResult uploadWithCertificate(String model,
63+
String filePath, String apiKey, OSSUploadCertificate certificate)
64+
throws NoApiKeyException {
3865
OkHttpClient client = OkHttpClientFactory.getOkHttpClient();
39-
DashScopeResult uploadInfo = get_upload_certificate(model, apiKey);
40-
JsonObject outputData = ((JsonObject) uploadInfo.getOutput()).getAsJsonObject("data");
66+
OSSUploadCertificate cert = certificate;
67+
68+
// Get certificate if not provided
69+
if (cert == null) {
70+
DashScopeResult uploadInfo = get_upload_certificate(model, apiKey);
71+
JsonObject outputData = ((JsonObject) uploadInfo.getOutput())
72+
.getAsJsonObject("data");
73+
cert = new OSSUploadCertificate(
74+
outputData.get("upload_host").getAsString(),
75+
outputData.get("oss_access_key_id").getAsString(),
76+
outputData.get("signature").getAsString(),
77+
outputData.get("policy").getAsString(),
78+
outputData.get("upload_dir").getAsString(),
79+
outputData.get("x_oss_object_acl").getAsString(),
80+
outputData.get("x_oss_forbid_overwrite").getAsString()
81+
);
82+
}
83+
4184
Map<String, String> headers = new HashMap<>();
4285
headers.put("user-agent", DashScopeHeaders.userAgent());
4386
headers.put("Accept", "application/json");
4487
File uploadFile = new File(filePath);
45-
String host = outputData.get("upload_host").getAsString();
46-
String ossAccessKeyId = outputData.get("oss_access_key_id").getAsString();
47-
String signature = outputData.get("signature").getAsString();
48-
String policy = outputData.get("policy").getAsString();
49-
String key = outputData.get("upload_dir").getAsString() + "/" + uploadFile.getName();
50-
String xOssObjectAcl = outputData.get("x_oss_object_acl").getAsString();
51-
String xOssForbidOverwrite = outputData.get("x_oss_forbid_overwrite").getAsString();
88+
String host = cert.getUploadHost();
89+
String ossAccessKeyId = cert.getOssAccessKeyId();
90+
String signature = cert.getSignature();
91+
String policy = cert.getPolicy();
92+
String key = cert.getUploadDir() + "/" + uploadFile.getName();
93+
String xOssObjectAcl = cert.getXOssObjectAcl();
94+
String xOssForbidOverwrite = cert.getXOssForbidOverwrite();
5295

5396
RequestBody requestBody =
5497
new MultipartBody.Builder()
@@ -67,13 +110,15 @@ public static String upload(String model, String filePath, String apiKey)
67110
RequestBody.create(MediaType.parse(getContentType(filePath)), uploadFile))
68111
.build();
69112

70-
Request request = new Request.Builder().url(host).post(requestBody).build();
113+
Request request = new Request.Builder().url(host).post(requestBody)
114+
.build();
71115
try (Response response = client.newCall(request).execute()) {
72116
if (!response.isSuccessful()) {
73117
Status status = parseFailed(response);
74118
throw new ApiException(status);
75119
}
76-
return String.format("oss://%s", key);
120+
String ossUrl = String.format("oss://%s", key);
121+
return new UploadResult(ossUrl, cert);
77122
} catch (Throwable e) {
78123
throw new ApiException(e);
79124
}

0 commit comments

Comments
 (0)