Voici un exemple d'implémentation permettant de regrouper plusieurs fichiers d'images (stockés en base de données) dans une archive ZIP pour téléchargement. L'extrait suivant illustre une approche possible avec Spring MVC.
Logique du contrôleur
Le contrôleur traite la requête de téléchargement groupé. Il récupère d'abord les noms des paquets correspondant à une famille et un type de produit, puis récupère les données binaires de chaque paquet, et enfin génère un fichier ZIP contenant tous ces paquets.
@Controller
public class ArchiveDownloadController {
@Autowired
private ImageArchiveService archiveService;
@RequestMapping(value = "/archive/batch", method = RequestMethod.GET)
public void handleBatchArchiveRequest(
HttpServletResponse response,
@RequestParam String family,
@RequestParam String type) throws IOException {
// Décodage des paramètres de requête
String decodedFamily = new String(family.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
String decodedType = new String(type.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
// Récupération des noms de paquets
List<String> archiveNames = archiveService.findArchiveNames(decodedFamily, decodedType);
if (archiveNames.isEmpty()) {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
return;
}
// Récupération des contenus des paquets
Map<String, byte[]> archiveData = archiveService.fetchArchives(archiveNames);
// Construction du nom du fichier ZIP
String zipFilename = decodedFamily + "_" + decodedType + ".zip";
// Génération du ZIP
response.setContentType("application/zip");
response.setHeader("Content-Disposition",
"attachment; filename=" + URLEncoder.encode(zipFilename, "UTF-8"));
try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
for (Map.Entry<String, byte[]> entry : archiveData.entrySet()) {
ZipEntry zipEntry = new ZipEntry(entry.getKey());
zos.putNextEntry(zipEntry);
zos.write(entry.getValue());
zos.closeEntry();
}
}
}
}
Service de gestion des archives
@Service
public class ImageArchiveService {
@Autowired
private ImageRepository imageRepository;
public List<String> findArchiveNames(String family, String type) {
return imageRepository.findArchiveNamesByCategory(family, type);
}
public Map<String, byte[]> fetchArchives(List<String> archiveNames) {
Map<String, byte[]> result = new HashMap<>();
for (String name : archiveNames) {
ImageArchive archive = imageRepository.findByArchiveName(name);
if (archive != null && archive.getBinaryContent() != null) {
result.put(name, archive.getBinaryContent());
}
}
return result;
}
}
Couche d'accès aux données
@Repository
public interface ImageRepository extends JpaRepository<ImageArchive, Long> {
@Query("SELECT a.archiveName FROM ImageArchive a " +
"JOIN a.product p " +
"WHERE p.productFamily = :family AND p.productType = :type")
List<String> findArchiveNamesByCategory(
@Param("family") String family,
@Param("type") String type);
ImageArchive findByArchiveName(String name);
}
Structure des entités
@Entity
@Table(name = "image_archives")
public class ImageArchive {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String archiveName;
@Lob
private byte[] binaryContent;
@ManyToOne
@JoinColumn(name = "product_id")
private Product product;
// Getters et setters
}
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String productFamily;
private String productType;
// Getters et setters
}