diff --git a/service/src/main/java/cn/qihangerp/service/external/pdd/PddPopClient.java b/service/src/main/java/cn/qihangerp/service/external/pdd/PddPopClient.java index dcd076ea..72330ba3 100644 --- a/service/src/main/java/cn/qihangerp/service/external/pdd/PddPopClient.java +++ b/service/src/main/java/cn/qihangerp/service/external/pdd/PddPopClient.java @@ -23,7 +23,7 @@ import java.util.Map; *
  • {@link #invokeGoodsAdd} — {@code pdd.goods.add}:业务字段全部在表单顶层({@code sku_list}、{@code carousel_gallery} 等为 JSON 字符串), * {@code param_json}、 {@code version}
  • *
  • {@link #invokeTopLevelBiz} — 如 {@code pdd.goods.cat.rule.get} 的 {@code cat_id}/{@code goods_id} 等顶层字段
  • - *
  • {@link #invokeGoodsImageUpload} — {@code pdd.goods.image.upload}:{@code application/x-www-form-urlencoded},{@code image} 为 Base64
  • + *
  • {@link #invokeGoodsImageUpload} — {@code pdd.goods.image.upload}:urlencoded,{@code image} 为 {@code data:image/*;base64,} + Base64
  • *
  • {@link #invoke} — 仅当接口要求整包 {@code param_json} 时使用(少数场景)
  • * */ @@ -97,9 +97,9 @@ public class PddPopClient { } /** - * {@code pdd.goods.image.upload}:与官方 curl/SDK 一致,{@code Content-Type: application/x-www-form-urlencoded}。 - *

    开放平台参数 {@code image}:类型 STRING、必填;支持 jpg/jpeg、png 等,须为图片二进制经 Base64 编码后的字符串。 - * 本方法对下载/读取得到的原始字节使用 {@link Base64#getEncoder()}(标准 Base64,无 {@code data:image/...;base64,} 前缀)。

    + * {@code pdd.goods.image.upload}:{@code application/x-www-form-urlencoded}。 + *

    参数 {@code image} 为必填 STRING:常见对接要求为 {@code data:image/jpeg;base64,} 或 {@code data:image/png;base64,} + * 后接标准 Base64(无换行)。仅传裸 Base64 时网关可能返回「图片格式错误」。

    */ 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 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 buildBaseParams(String clientId, String accessToken, String type, boolean withVersion) { long ts = System.currentTimeMillis() / 1000L;