Refactor PrestashopClient to enhance manual multipart image upload handling and improve error logging
This commit is contained in:
@@ -2,21 +2,22 @@ package fr.gameovergne.api.service.prestashop;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.core.io.ByteArrayResource;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.*;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.client.MultipartBodyBuilder;
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.web.client.HttpClientErrorException;
|
|
||||||
import org.springframework.web.client.RestClient;
|
import org.springframework.web.client.RestClient;
|
||||||
import org.springframework.web.client.RestClientException;
|
import org.springframework.web.client.RestClientException;
|
||||||
import org.springframework.web.client.RestClientResponseException;
|
import org.springframework.web.client.RestClientResponseException;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.*;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
|
||||||
@@ -232,8 +233,6 @@ public class PrestashopClient {
|
|||||||
/**
|
/**
|
||||||
* Upload d'une image produit vers PrestaShop :
|
* Upload d'une image produit vers PrestaShop :
|
||||||
* POST /api/images/products/{productId}
|
* POST /api/images/products/{productId}
|
||||||
* <p>
|
|
||||||
* On envoie directement les bytes du fichier avec le bon content-type (image/jpeg, image/png, ...).
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public ResponseEntity<String> uploadProductImage(
|
public ResponseEntity<String> uploadProductImage(
|
||||||
@@ -242,7 +241,7 @@ public class PrestashopClient {
|
|||||||
MultipartFile imageFile
|
MultipartFile imageFile
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
// Construire l’URL Presta
|
// Construire l’URL Presta (comme avant)
|
||||||
StringBuilder urlBuilder = new StringBuilder(baseUrl)
|
StringBuilder urlBuilder = new StringBuilder(baseUrl)
|
||||||
.append("/images/products/")
|
.append("/images/products/")
|
||||||
.append(productId);
|
.append(productId);
|
||||||
@@ -252,7 +251,7 @@ public class PrestashopClient {
|
|||||||
}
|
}
|
||||||
String url = urlBuilder.toString();
|
String url = urlBuilder.toString();
|
||||||
|
|
||||||
byte[] bytes = imageFile.getBytes();
|
byte[] fileBytes = imageFile.getBytes();
|
||||||
|
|
||||||
String originalFilename = imageFile.getOriginalFilename();
|
String originalFilename = imageFile.getOriginalFilename();
|
||||||
if (originalFilename == null || originalFilename.isBlank()) {
|
if (originalFilename == null || originalFilename.isBlank()) {
|
||||||
@@ -261,42 +260,86 @@ public class PrestashopClient {
|
|||||||
|
|
||||||
String contentType = imageFile.getContentType();
|
String contentType = imageFile.getContentType();
|
||||||
if (contentType == null || contentType.isBlank()) {
|
if (contentType == null || contentType.isBlank()) {
|
||||||
contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
|
contentType = "application/octet-stream";
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
"[PrestaShop] POST (image multipart) {} (size={} bytes, contentType={}, filename={})",
|
"[PrestaShop] POST (image multipart - manual) {} (size={} bytes, contentType={}, filename={})",
|
||||||
url, bytes.length, contentType, originalFilename
|
url, fileBytes.length, contentType, originalFilename
|
||||||
);
|
);
|
||||||
|
|
||||||
// Resource avec filename pour que PHP le traite comme un fichier dans $_FILES["image"]
|
// -------- Construction du multipart "à la main" --------
|
||||||
String finalOriginalFilename = originalFilename;
|
String boundary = "----PrestashopBoundary" + System.currentTimeMillis();
|
||||||
ByteArrayResource imageResource = new ByteArrayResource(bytes) {
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
@Override
|
OutputStreamWriter osw = new OutputStreamWriter(baos, StandardCharsets.UTF_8);
|
||||||
public String getFilename() {
|
PrintWriter writer = new PrintWriter(osw, true);
|
||||||
return finalOriginalFilename;
|
|
||||||
|
// Début de la part "image"
|
||||||
|
writer.append("--").append(boundary).append("\r\n");
|
||||||
|
writer.append("Content-Disposition: form-data; name=\"image\"; filename=\"")
|
||||||
|
.append(originalFilename)
|
||||||
|
.append("\"\r\n");
|
||||||
|
writer.append("Content-Type: ").append(contentType).append("\r\n");
|
||||||
|
writer.append("\r\n");
|
||||||
|
writer.flush();
|
||||||
|
|
||||||
|
// Données binaires du fichier
|
||||||
|
baos.write(fileBytes);
|
||||||
|
baos.write("\r\n".getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
// Fin du multipart
|
||||||
|
writer.append("--").append(boundary).append("--").append("\r\n");
|
||||||
|
writer.flush();
|
||||||
|
|
||||||
|
byte[] multipartBytes = baos.toByteArray();
|
||||||
|
|
||||||
|
// -------- Envoi via HttpURLConnection --------
|
||||||
|
URL targetUrl = URI.create(url).toURL();
|
||||||
|
HttpURLConnection conn = (HttpURLConnection) targetUrl.openConnection();
|
||||||
|
conn.setDoOutput(true);
|
||||||
|
conn.setRequestMethod("POST");
|
||||||
|
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
|
||||||
|
conn.setRequestProperty("Accept", "application/xml, text/xml, */*;q=0.1");
|
||||||
|
|
||||||
|
// Important : pas de chunked, on envoie une taille fixe
|
||||||
|
conn.setFixedLengthStreamingMode(multipartBytes.length);
|
||||||
|
|
||||||
|
try (OutputStream os = conn.getOutputStream()) {
|
||||||
|
os.write(multipartBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
int status = conn.getResponseCode();
|
||||||
|
|
||||||
|
InputStream is = (status >= 200 && status < 300)
|
||||||
|
? conn.getInputStream()
|
||||||
|
: conn.getErrorStream();
|
||||||
|
|
||||||
|
String responseBody;
|
||||||
|
if (is != null) {
|
||||||
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
sb.append(line).append("\n");
|
||||||
|
}
|
||||||
|
responseBody = sb.toString();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
responseBody = "";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
log.info("[PrestaShop] Image upload response status={}, body={}", status, responseBody);
|
||||||
public long contentLength() {
|
|
||||||
return bytes.length;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// body multipart : la clé "image" => part nommée "image"
|
HttpStatus springStatus = HttpStatus.resolve(status);
|
||||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
if (springStatus == null) {
|
||||||
body.add("image", imageResource);
|
springStatus = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
return client.post()
|
return new ResponseEntity<>(responseBody, springStatus);
|
||||||
.uri(URI.create(url))
|
|
||||||
.contentType(MediaType.MULTIPART_FORM_DATA)
|
|
||||||
.body(body)
|
|
||||||
.retrieve()
|
|
||||||
.toEntity(String.class);
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("[PrestaShop] Erreur lecture fichier image", e);
|
log.error("[PrestaShop] Erreur lors de l'upload d'image", e);
|
||||||
throw new RuntimeException("Erreur lors de la lecture du fichier image", e);
|
throw new RuntimeException("Erreur lors de l'upload d'image vers PrestaShop", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user