fix(pdd): goods.image.upload 的 image 使用 data:image/*;base64, 前缀
拼多多常见报「图片格式错误」因仅传裸 Base64;按魔数选 jpeg/png/gif/webp/bmp 后拼接 data URI。 Made-with: Cursor
This commit is contained in:
parent
cc20c98168
commit
7b2d6b4894
|
|
@ -23,7 +23,7 @@ import java.util.Map;
|
|||
* <li>{@link #invokeGoodsAdd} — {@code pdd.goods.add}:业务字段全部在表单顶层({@code sku_list}、{@code carousel_gallery} 等为 JSON 字符串),
|
||||
* <b>无</b> {@code param_json}、<b>无</b> {@code version}</li>
|
||||
* <li>{@link #invokeTopLevelBiz} — 如 {@code pdd.goods.cat.rule.get} 的 {@code cat_id}/{@code goods_id} 等顶层字段</li>
|
||||
* <li>{@link #invokeGoodsImageUpload} — {@code pdd.goods.image.upload}:{@code application/x-www-form-urlencoded},{@code image} 为 Base64</li>
|
||||
* <li>{@link #invokeGoodsImageUpload} — {@code pdd.goods.image.upload}:urlencoded,{@code image} 为 {@code data:image/*;base64,} + Base64</li>
|
||||
* <li>{@link #invoke} — 仅当接口要求整包 {@code param_json} 时使用(少数场景)</li>
|
||||
* </ul>
|
||||
*/
|
||||
|
|
@ -97,9 +97,9 @@ public class PddPopClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* {@code pdd.goods.image.upload}:与官方 curl/SDK 一致,{@code Content-Type: application/x-www-form-urlencoded}。
|
||||
* <p>开放平台参数 {@code image}:类型 STRING、<b>必填</b>;支持 jpg/jpeg、png 等,<b>须为图片二进制经 Base64 编码后的字符串</b>。
|
||||
* 本方法对下载/读取得到的原始字节使用 {@link Base64#getEncoder()}(标准 Base64,无 {@code data:image/...;base64,} 前缀)。</p>
|
||||
* {@code pdd.goods.image.upload}:{@code application/x-www-form-urlencoded}。
|
||||
* <p>参数 {@code image} 为必填 STRING:常见对接要求为 {@code data:image/jpeg;base64,} 或 {@code data:image/png;base64,}
|
||||
* 后接<strong>标准 Base64</strong>(无换行)。仅传裸 Base64 时网关可能返回「图片格式错误」。</p>
|
||||
*/
|
||||
public String invokeGoodsImageUpload(String gatewayUrl, String clientId, String clientSecret, String accessToken,
|
||||
byte[] imageBytes) throws Exception {
|
||||
|
|
@ -109,14 +109,48 @@ public class PddPopClient {
|
|||
if (imageBytes == null || imageBytes.length == 0) {
|
||||
throw new IllegalArgumentException("image 不能为空");
|
||||
}
|
||||
String b64 = Base64.getEncoder().encodeToString(imageBytes);
|
||||
String imageParam = buildPddGoodsImageUploadParam(imageBytes);
|
||||
Map<String, String> params = buildBaseParams(clientId, accessToken, "pdd.goods.image.upload", false);
|
||||
params.put("image", b64);
|
||||
String logPayload = "pdd.goods.image.upload image=base64(len=" + b64.length() + ") rawBytes=" + imageBytes.length;
|
||||
params.put("image", imageParam);
|
||||
String logPayload = "pdd.goods.image.upload image=data:*;base64,(totalChars=" + imageParam.length() + ") rawBytes="
|
||||
+ imageBytes.length;
|
||||
return postSignedAndLog(gatewayUrl, clientId, clientSecret, "pdd.goods.image.upload", params, logPayload,
|
||||
Duration.ofSeconds(180));
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装 {@code pdd.goods.image.upload} 的 {@code image} 字段:{@code data:{mime};base64,}{base64}。
|
||||
*/
|
||||
static String buildPddGoodsImageUploadParam(byte[] imageBytes) {
|
||||
String mime = guessImageMimeForPddUpload(imageBytes);
|
||||
String b64 = Base64.getEncoder().encodeToString(imageBytes);
|
||||
return "data:" + mime + ";base64," + b64;
|
||||
}
|
||||
|
||||
/** 按文件头识别 MIME;无法识别时按 jpeg 声明(仍可能因非图片内容被平台拒绝)。 */
|
||||
private static String guessImageMimeForPddUpload(byte[] b) {
|
||||
if (b == null || b.length < 12) {
|
||||
return "image/jpeg";
|
||||
}
|
||||
if (b[0] == (byte) 0xFF && b[1] == (byte) 0xD8 && b[2] == (byte) 0xFF) {
|
||||
return "image/jpeg";
|
||||
}
|
||||
if (b[0] == (byte) 0x89 && b[1] == 'P' && b[2] == 'N' && b[3] == 'G') {
|
||||
return "image/png";
|
||||
}
|
||||
if (b[0] == 'G' && b[1] == 'I' && b[2] == 'F') {
|
||||
return "image/gif";
|
||||
}
|
||||
if (b[0] == 'R' && b[1] == 'I' && b[2] == 'F' && b[3] == 'F'
|
||||
&& b[8] == 'W' && b[9] == 'E' && b[10] == 'B' && b[11] == 'P') {
|
||||
return "image/webp";
|
||||
}
|
||||
if (b[0] == 'B' && b[1] == 'M') {
|
||||
return "image/bmp";
|
||||
}
|
||||
return "image/jpeg";
|
||||
}
|
||||
|
||||
private static Map<String, String> buildBaseParams(String clientId, String accessToken, String type,
|
||||
boolean withVersion) {
|
||||
long ts = System.currentTimeMillis() / 1000L;
|
||||
|
|
|
|||
Loading…
Reference in New Issue