Skip to content

Commit 145ce9d

Browse files
authored
Merge pull request #11429 from IQSS/11382-featured-items-unicode-filename
Allow images with unicode filenames to be uploaded for featured items
2 parents 416b02e + 3b6f294 commit 145ce9d

File tree

9 files changed

+146
-3
lines changed

9 files changed

+146
-3
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/bin/sh
2+
export API_TOKEN=e62ac03b-3bc8-4194-bcd6-d0381fe84e39
3+
export SERVER_URL=http://localhost:8080
4+
export ID=:root
5+
export FILENAME='π.png'
6+
export CONTENT='test1'
7+
export DISPLAY_ORDER=0
8+
9+
curl -H "X-Dataverse-key:$API_TOKEN" \
10+
-X POST \
11+
-F "id=0" \
12+
-F "content=$CONTENT" \
13+
-F "displayOrder=$DISPLAY_ORDER" \
14+
-F "fileName=$FILENAME" \
15+
-F "keepFile=false" \
16+
-F "file=@$FILENAME" \
17+
"$SERVER_URL/api/dataverses/$ID/featuredItems"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/bin/bash -x
2+
export API_TOKEN=e62ac03b-3bc8-4194-bcd6-d0381fe84e39
3+
export SERVER_URL=http://localhost:8080
4+
export ID=78
5+
export FILENAME='π.png'
6+
export CONTENT='test1'
7+
export DISPLAY_ORDER=0
8+
9+
curl -H "X-Dataverse-key:$API_TOKEN" \
10+
-X PUT \
11+
-F "id=0" \
12+
-F "content=$CONTENT" \
13+
-F "displayOrder=$DISPLAY_ORDER" \
14+
-F "fileName=$FILENAME" \
15+
-F "keepFile=false" \
16+
-F "file=@$FILENAME" \
17+
"$SERVER_URL/api/dataverseFeaturedItems/$ID"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/bin/sh
2+
export API_TOKEN=e62ac03b-3bc8-4194-bcd6-d0381fe84e39
3+
export SERVER_URL=http://localhost:8080
4+
export ID=:root
5+
export FILENAME='π.png'
6+
export CONTENT='test1'
7+
export DISPLAY_ORDER=0
8+
9+
curl -H "X-Dataverse-key:$API_TOKEN" \
10+
-X PUT \
11+
-F "id=0" \
12+
-F "content=$CONTENT" \
13+
-F "displayOrder=$DISPLAY_ORDER" \
14+
-F "fileName=$FILENAME" \
15+
-F "keepFile=false" \
16+
-F "file=@$FILENAME" \
17+
"$SERVER_URL/api/dataverses/$ID/featuredItems"

scripts/issues/11429/π.png

4.78 KB
Loading

src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
3030
import edu.harvard.iq.dataverse.util.BundleUtil;
3131
import edu.harvard.iq.dataverse.util.ConstraintViolationUtil;
32+
import edu.harvard.iq.dataverse.util.FileUtil;
3233
import edu.harvard.iq.dataverse.util.StringUtil;
3334
import static edu.harvard.iq.dataverse.util.StringUtil.nonEmpty;
3435
import static edu.harvard.iq.dataverse.util.json.JsonPrinter.*;
@@ -1863,7 +1864,7 @@ public Response updateFeaturedItems(
18631864

18641865
if (files != null) {
18651866
Optional<FormDataBodyPart> matchingFile = files.stream()
1866-
.filter(file -> file.getFormDataContentDisposition().getFileName().equals(fileName))
1867+
.filter(file -> fileName.equals(FileUtil.decodeFileName(file.getFormDataContentDisposition().getFileName())))
18671868
.findFirst();
18681869

18691870
if (matchingFile.isPresent()) {

src/main/java/edu/harvard/iq/dataverse/api/dto/NewDataverseFeaturedItemDTO.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package edu.harvard.iq.dataverse.api.dto;
22

3+
import edu.harvard.iq.dataverse.util.FileUtil;
34
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
45

56
import java.io.InputStream;
@@ -21,7 +22,7 @@ public static NewDataverseFeaturedItemDTO fromFormData(String content,
2122

2223
if (imageFileInputStream != null) {
2324
newDataverseFeaturedItemDTO.imageFileInputStream = imageFileInputStream;
24-
newDataverseFeaturedItemDTO.imageFileName = contentDispositionHeader.getFileName();
25+
newDataverseFeaturedItemDTO.imageFileName = FileUtil.decodeFileName(contentDispositionHeader.getFileName());
2526
}
2627

2728
return newDataverseFeaturedItemDTO;

src/main/java/edu/harvard/iq/dataverse/api/dto/UpdatedDataverseFeaturedItemDTO.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package edu.harvard.iq.dataverse.api.dto;
22

3+
import edu.harvard.iq.dataverse.util.FileUtil;
34
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
45

56
import java.io.InputStream;
@@ -24,7 +25,7 @@ public static UpdatedDataverseFeaturedItemDTO fromFormData(String content,
2425

2526
if (imageFileInputStream != null) {
2627
updatedDataverseFeaturedItemDTO.imageFileInputStream = imageFileInputStream;
27-
updatedDataverseFeaturedItemDTO.imageFileName = contentDispositionHeader.getFileName();
28+
updatedDataverseFeaturedItemDTO.imageFileName = FileUtil.decodeFileName(contentDispositionHeader.getFileName());
2829
}
2930

3031
return updatedDataverseFeaturedItemDTO;

src/main/java/edu/harvard/iq/dataverse/util/FileUtil.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
import edu.harvard.iq.dataverse.dataaccess.DataAccessOption;
102102
import edu.harvard.iq.dataverse.dataaccess.StorageIO;
103103
import edu.harvard.iq.dataverse.util.file.FileExceedsStorageQuotaException;
104+
import java.nio.charset.StandardCharsets;
104105
import java.util.Arrays;
105106
import org.apache.commons.io.IOUtils;
106107
import org.apache.commons.lang3.StringUtils;
@@ -1881,4 +1882,19 @@ public static boolean isFileOfImageType(File file) throws IOException {
18811882
String mimeType = tika.detect(file);
18821883
return mimeType != null && mimeType.startsWith("image/");
18831884
}
1885+
1886+
/**
1887+
* Converts a filename from ISO_8859_1 to UTF_8 to handle encoding issues.
1888+
* Ref: https://github.com/eclipse-ee4j/jersey/issues/1700
1889+
*
1890+
* @param originalFileName the original filename
1891+
* @return the filename converted to UTF_8
1892+
*/
1893+
public static String decodeFileName(String originalFileName) {
1894+
if (originalFileName == null) {
1895+
return null;
1896+
}
1897+
return new String(originalFileName.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
1898+
}
1899+
18841900
}

src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@
1010
import java.text.MessageFormat;
1111

1212
import static jakarta.ws.rs.core.Response.Status.*;
13+
import java.io.File;
14+
import java.io.IOException;
15+
import java.nio.file.Path;
16+
import java.util.Arrays;
17+
import java.util.List;
18+
import java.util.logging.Level;
19+
import java.util.logging.Logger;
20+
import static org.hamcrest.CoreMatchers.containsString;
1321
import static org.hamcrest.CoreMatchers.equalTo;
1422

1523
public class DataverseFeaturedItemsIT {
@@ -84,6 +92,71 @@ public void testUpdateFeaturedItem() {
8492
verifyUpdatedFeaturedItem(updateFeatureItemResponse, sanitizedContent, "coffeeshop.png", 2);
8593
}
8694

95+
@Test
96+
public void testUpdateFeaturedItemUnicode() {
97+
String apiToken = createUserAndGetApiToken();
98+
String dataverseAlias = createDataverseAndGetAlias(apiToken);
99+
100+
String coffeeShopEnglish = "src/test/resources/images/coffeeshop.png";
101+
String coffeeShopGreek = System.getProperty("java.io.tmpdir") + File.separator + "καφενείο.png";
102+
Path pathToCoffeeShopGreek = java.nio.file.Paths.get(coffeeShopGreek);
103+
System.out.println("path to coffee show in Greek: " + pathToCoffeeShopGreek);
104+
try {
105+
java.nio.file.Files.copy(java.nio.file.Paths.get(coffeeShopEnglish), pathToCoffeeShopGreek, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
106+
} catch (IOException ex) {
107+
Logger.getLogger(DataverseFeaturedItemsIT.class.getName()).log(Level.SEVERE, null, ex);
108+
}
109+
110+
Response createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, "test", 0, coffeeShopGreek);
111+
createFeatureItemResponse.prettyPrint();
112+
/**
113+
* TODO: Fix this REST Assured test. Sending unicode works fine in curl
114+
* (see scripts/issues/11429/add-featured-items.sh) and we suspect we
115+
* aren't sending Unicode properly through REST Assured (or Unicode
116+
* isn't supported, which isn't likely). For now we assert
117+
* "????????.png" but once we fix the test, "καφενείο.png" should be
118+
* asserted.
119+
*/
120+
verifyUpdatedFeaturedItem(createFeatureItemResponse, "test", "????????.png", 0);
121+
122+
long featuredItemId = JsonPath.from(createFeatureItemResponse.body().asString()).getLong("data.id");
123+
124+
// update content
125+
Response updateFeatureItemResponse = UtilIT.updateDataverseFeaturedItem(featuredItemId, "updatedTitle1", 1, true, null, apiToken);
126+
updateFeatureItemResponse.prettyPrint();
127+
// TODO: Fix this REST Assured assertion too (see above).
128+
// The equivalent curl command: scripts/issues/11429/update-featured-item.sh
129+
verifyUpdatedFeaturedItem(updateFeatureItemResponse, "updatedTitle1", "????????.png", 1);
130+
131+
// remove image
132+
updateFeatureItemResponse = UtilIT.updateDataverseFeaturedItem(featuredItemId, "updatedTitle1", 2, false, null, apiToken);
133+
verifyUpdatedFeaturedItem(updateFeatureItemResponse, "updatedTitle1", null, 2);
134+
135+
// add non-unicode image
136+
updateFeatureItemResponse = UtilIT.updateDataverseFeaturedItem(featuredItemId, "updatedTitle1", 2, false, coffeeShopEnglish, apiToken);
137+
verifyUpdatedFeaturedItem(updateFeatureItemResponse, "updatedTitle1", "coffeeshop.png", 2);
138+
139+
updateFeatureItemResponse = UtilIT.deleteDataverseFeaturedItem(featuredItemId, apiToken);
140+
updateFeatureItemResponse.then().assertThat().statusCode(OK.getStatusCode());
141+
142+
List<Long> ids = Arrays.asList(0L);
143+
List<String> contents = Arrays.asList("Greek filename");
144+
List<Integer> orders = Arrays.asList(0);
145+
List<Boolean> keepFiles = Arrays.asList(false);
146+
List<String> pathsToFiles = Arrays.asList(coffeeShopGreek);
147+
148+
Response updateDataverseFeaturedItemsResponse = UtilIT.updateDataverseFeaturedItems(dataverseAlias, ids, contents, orders, keepFiles, pathsToFiles, apiToken);
149+
updateDataverseFeaturedItemsResponse.prettyPrint();
150+
updateDataverseFeaturedItemsResponse.then().assertThat()
151+
.statusCode(OK.getStatusCode())
152+
.body("data[0].content", equalTo("Greek filename"))
153+
// TODO: Fix this REST Assured assertion too (see above).
154+
// The equivalent curl command: scripts/issues/11429/update-featured-items.sh
155+
.body("data[0].imageFileName", equalTo("????????.png"))
156+
.body("data[0].imageFileUrl", containsString("/api/access/dataverseFeaturedItemImage/"))
157+
.body("data[0].displayOrder", equalTo(0));
158+
}
159+
87160
private String createUserAndGetApiToken() {
88161
Response createUserResponse = UtilIT.createRandomUser();
89162
return UtilIT.getApiTokenFromResponse(createUserResponse);

0 commit comments

Comments
 (0)