diff --git a/api/src/main/java/fr/gameovergne/api/service/prestashop/PrestashopClient.java b/api/src/main/java/fr/gameovergne/api/service/prestashop/PrestashopClient.java index 32fec53..a06fe03 100644 --- a/api/src/main/java/fr/gameovergne/api/service/prestashop/PrestashopClient.java +++ b/api/src/main/java/fr/gameovergne/api/service/prestashop/PrestashopClient.java @@ -2,21 +2,22 @@ package fr.gameovergne.api.service.prestashop; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.io.ByteArrayResource; -import org.springframework.http.*; -import org.springframework.http.client.MultipartBodyBuilder; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; -import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestClientResponseException; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.util.UriComponentsBuilder; -import java.io.IOException; +import java.io.*; +import java.net.HttpURLConnection; import java.net.URI; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Base64; @@ -232,8 +233,6 @@ public class PrestashopClient { /** * Upload d'une image produit vers PrestaShop : * POST /api/images/products/{productId} - *

- * On envoie directement les bytes du fichier avec le bon content-type (image/jpeg, image/png, ...). */ public ResponseEntity uploadProductImage( @@ -242,7 +241,7 @@ public class PrestashopClient { MultipartFile imageFile ) { try { - // Construire l’URL Presta + // Construire l’URL Presta (comme avant) StringBuilder urlBuilder = new StringBuilder(baseUrl) .append("/images/products/") .append(productId); @@ -252,7 +251,7 @@ public class PrestashopClient { } String url = urlBuilder.toString(); - byte[] bytes = imageFile.getBytes(); + byte[] fileBytes = imageFile.getBytes(); String originalFilename = imageFile.getOriginalFilename(); if (originalFilename == null || originalFilename.isBlank()) { @@ -261,42 +260,86 @@ public class PrestashopClient { String contentType = imageFile.getContentType(); if (contentType == null || contentType.isBlank()) { - contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE; + contentType = "application/octet-stream"; } log.info( - "[PrestaShop] POST (image multipart) {} (size={} bytes, contentType={}, filename={})", - url, bytes.length, contentType, originalFilename + "[PrestaShop] POST (image multipart - manual) {} (size={} bytes, contentType={}, filename={})", + url, fileBytes.length, contentType, originalFilename ); - // Resource avec filename pour que PHP le traite comme un fichier dans $_FILES["image"] - String finalOriginalFilename = originalFilename; - ByteArrayResource imageResource = new ByteArrayResource(bytes) { - @Override - public String getFilename() { - return finalOriginalFilename; + // -------- Construction du multipart "à la main" -------- + String boundary = "----PrestashopBoundary" + System.currentTimeMillis(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStreamWriter osw = new OutputStreamWriter(baos, StandardCharsets.UTF_8); + PrintWriter writer = new PrintWriter(osw, true); + + // 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 - public long contentLength() { - return bytes.length; - } - }; + log.info("[PrestaShop] Image upload response status={}, body={}", status, responseBody); - // body multipart : la clé "image" => part nommée "image" - MultiValueMap body = new LinkedMultiValueMap<>(); - body.add("image", imageResource); + HttpStatus springStatus = HttpStatus.resolve(status); + if (springStatus == null) { + springStatus = HttpStatus.INTERNAL_SERVER_ERROR; + } - return client.post() - .uri(URI.create(url)) - .contentType(MediaType.MULTIPART_FORM_DATA) - .body(body) - .retrieve() - .toEntity(String.class); + return new ResponseEntity<>(responseBody, springStatus); } catch (IOException e) { - log.error("[PrestaShop] Erreur lecture fichier image", e); - throw new RuntimeException("Erreur lors de la lecture du fichier image", e); + log.error("[PrestaShop] Erreur lors de l'upload d'image", e); + throw new RuntimeException("Erreur lors de l'upload d'image vers PrestaShop", e); } } } \ No newline at end of file