From d20cf5bb81252717518c640b7be9e443dee177d0 Mon Sep 17 00:00:00 2001 From: tylerjmchugh <163562062+tylerjmchugh@users.noreply.github.com> Date: Tue, 10 Dec 2024 10:37:28 -0500 Subject: [PATCH] [Backport 4.2.x] Attachments API use filename from Content-Disposition before url (#8470) * Update spring doc consumes and produces * Update putResource logic to get filename from contentDisposition header before using url * retrigger checks * retrigger checks * Remove incorrect consumes * Update getFilenameFromHeader to return null on exception * Update getFilenameFromHeader to return null on exception (including fileUrl.openConnection) --- .../records/attachments/AbstractStore.java | 34 ++++++++++++++++++- .../records/attachments/AttachmentsApi.java | 5 +-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/fao/geonet/api/records/attachments/AbstractStore.java b/core/src/main/java/org/fao/geonet/api/records/attachments/AbstractStore.java index 35d1b2867a7..e9b6a59593e 100644 --- a/core/src/main/java/org/fao/geonet/api/records/attachments/AbstractStore.java +++ b/core/src/main/java/org/fao/geonet/api/records/attachments/AbstractStore.java @@ -35,11 +35,15 @@ import org.fao.geonet.kernel.AccessManager; import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.repository.MetadataRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.web.multipart.MultipartFile; import java.io.BufferedInputStream; +import java.io.IOException; import java.io.InputStream; +import java.net.HttpURLConnection; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; @@ -53,6 +57,7 @@ public abstract class AbstractStore implements Store { protected static final String RESOURCE_MANAGEMENT_EXTERNAL_PROPERTIES_SEPARATOR = ":"; protected static final String RESOURCE_MANAGEMENT_EXTERNAL_PROPERTIES_ESCAPED_SEPARATOR = "\\:"; + private static final Logger log = LoggerFactory.getLogger(AbstractStore.class); @Override public final List getResources(final ServiceContext context, final String metadataUuid, final Sort sort, @@ -152,6 +157,29 @@ protected int canDownload(ServiceContext context, String metadataUuid, MetadataR return metadataId; } + protected String getFilenameFromHeader(final URL fileUrl) throws IOException { + HttpURLConnection connection = null; + try { + connection = (HttpURLConnection) fileUrl.openConnection(); + connection.setRequestMethod("HEAD"); + connection.connect(); + String contentDisposition = connection.getHeaderField("Content-Disposition"); + + if (contentDisposition != null && contentDisposition.contains("filename=")) { + String filename = contentDisposition.split("filename=")[1].replace("\"", "").trim(); + return filename.isEmpty() ? null : filename; + } + return null; + } catch (Exception e) { + log.error("Error retrieving resource filename from header", e); + return null; + } finally { + if (connection != null) { + connection.disconnect(); + } + } + } + protected String getFilenameFromUrl(final URL fileUrl) { String fileName = FilenameUtils.getName(fileUrl.getPath()); if (fileName.contains("?")) { @@ -198,7 +226,11 @@ public final MetadataResource putResource(ServiceContext context, String metadat @Override public final MetadataResource putResource(ServiceContext context, String metadataUuid, URL fileUrl, MetadataResourceVisibility visibility, Boolean approved) throws Exception { - return putResource(context, metadataUuid, getFilenameFromUrl(fileUrl), fileUrl.openStream(), null, visibility, approved); + String filename = getFilenameFromHeader(fileUrl); + if (filename == null) { + filename = getFilenameFromUrl(fileUrl); + } + return putResource(context, metadataUuid, filename, fileUrl.openStream(), null, visibility, approved); } @Override diff --git a/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsApi.java b/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsApi.java index b1b27ecfa30..b3d3beafd62 100644 --- a/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsApi.java @@ -200,7 +200,7 @@ public void delResources( @io.swagger.v3.oas.annotations.Operation(summary = "Create a new resource for a given metadata") @PreAuthorize("hasAuthority('Editor')") @RequestMapping(method = RequestMethod.POST, - consumes = MediaType.ALL_VALUE, + consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(value = HttpStatus.CREATED) @ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Attachment uploaded."), @@ -228,7 +228,8 @@ public MetadataResource putResource( @io.swagger.v3.oas.annotations.Operation(summary = "Create a new resource from a URL for a given metadata") @PreAuthorize("hasAuthority('Editor')") - @RequestMapping(method = RequestMethod.PUT) + @RequestMapping(method = RequestMethod.PUT, + produces = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(value = HttpStatus.CREATED) @ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Attachment added."), @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_CAN_EDIT)})