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 字符串),
|
* <li>{@link #invokeGoodsAdd} — {@code pdd.goods.add}:业务字段全部在表单顶层({@code sku_list}、{@code carousel_gallery} 等为 JSON 字符串),
|
||||||
* <b>无</b> {@code param_json}、<b>无</b> {@code version}</li>
|
* <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 #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>
|
* <li>{@link #invoke} — 仅当接口要求整包 {@code param_json} 时使用(少数场景)</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
|
|
@ -97,9 +97,9 @@ public class PddPopClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code pdd.goods.image.upload}:与官方 curl/SDK 一致,{@code Content-Type: application/x-www-form-urlencoded}。
|
* {@code pdd.goods.image.upload}:{@code application/x-www-form-urlencoded}。
|
||||||
* <p>开放平台参数 {@code image}:类型 STRING、<b>必填</b>;支持 jpg/jpeg、png 等,<b>须为图片二进制经 Base64 编码后的字符串</b>。
|
* <p>参数 {@code image} 为必填 STRING:常见对接要求为 {@code data:image/jpeg;base64,} 或 {@code data:image/png;base64,}
|
||||||
* 本方法对下载/读取得到的原始字节使用 {@link Base64#getEncoder()}(标准 Base64,无 {@code data:image/...;base64,} 前缀)。</p>
|
* 后接<strong>标准 Base64</strong>(无换行)。仅传裸 Base64 时网关可能返回「图片格式错误」。</p>
|
||||||
*/
|
*/
|
||||||
public String invokeGoodsImageUpload(String gatewayUrl, String clientId, String clientSecret, String accessToken,
|
public String invokeGoodsImageUpload(String gatewayUrl, String clientId, String clientSecret, String accessToken,
|
||||||
byte[] imageBytes) throws Exception {
|
byte[] imageBytes) throws Exception {
|
||||||
|
|
@ -109,14 +109,48 @@ public class PddPopClient {
|
||||||
if (imageBytes == null || imageBytes.length == 0) {
|
if (imageBytes == null || imageBytes.length == 0) {
|
||||||
throw new IllegalArgumentException("image 不能为空");
|
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);
|
Map<String, String> params = buildBaseParams(clientId, accessToken, "pdd.goods.image.upload", false);
|
||||||
params.put("image", b64);
|
params.put("image", imageParam);
|
||||||
String logPayload = "pdd.goods.image.upload image=base64(len=" + b64.length() + ") rawBytes=" + imageBytes.length;
|
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,
|
return postSignedAndLog(gatewayUrl, clientId, clientSecret, "pdd.goods.image.upload", params, logPayload,
|
||||||
Duration.ofSeconds(180));
|
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,
|
private static Map<String, String> buildBaseParams(String clientId, String accessToken, String type,
|
||||||
boolean withVersion) {
|
boolean withVersion) {
|
||||||
long ts = System.currentTimeMillis() / 1000L;
|
long ts = System.currentTimeMillis() / 1000L;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue