liubao68 closed pull request #670: [SCB-512] download support chinese and space in file name URL: https://github.com/apache/incubator-servicecomb-java-chassis/pull/670
This is a PR merged from a forked repository. As GitHub hides the original diff on merge, it is displayed below for the sake of provenance: As this is a foreign pull request (from a fork), the diff is supplied below (as it won't show otherwise due to GitHub magic): diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java index 2303cc01b..79aedb665 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java @@ -113,7 +113,7 @@ protected void testAllTransport(String microserviceName, RestTemplate template, private void testUpload(RestTemplate template, String cseUrlPrefix) throws IOException { String file1Content = "hello world"; - File file1 = File.createTempFile("upload1", ".txt"); + File file1 = File.createTempFile("测 试", ".txt"); FileUtils.writeStringToFile(file1, file1Content); String file2Content = " bonjour"; diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownload.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownload.java index 303c00142..6472171ed 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownload.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownload.java @@ -98,9 +98,15 @@ public void runRest() { futures.add(checkFile(intf.file(content))); futures.add(checkFuture(templateGet("file").saveAsString())); - // fix in next PR - // futures.add(checkFile(intf.chineseAndSpaceFile(content))); - // futures.add(checkFuture(templateGet("chineseAndSpaceFile").saveAsString())); + { + ReadStreamPart part = intf.chineseAndSpaceFile(content); + TestMgr.check("测 试.test.txt", part.getSubmittedFileName()); + futures.add(checkFile(part)); + + part = templateGet("chineseAndSpaceFile"); + TestMgr.check("测 试.test.txt", part.getSubmittedFileName()); + futures.add(checkFuture(part.saveAsString())); + } futures.add(checkFile(intf.resource(content))); futures.add(checkFuture(templateGet("resource").saveAsString())); diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpUtils.java index deb6a977d..81c27a489 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpUtils.java @@ -17,10 +17,8 @@ package org.apache.servicecomb.foundation.common.http; import java.io.File; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; +import java.net.URI; +import java.net.URISyntaxException; import org.springframework.util.StringUtils; @@ -52,34 +50,35 @@ public static String parseParamFromHeaderValue(String headerValue, String paramN return null; } - public static String uriEncode(String value) { - return uriEncode(value, StandardCharsets.UTF_8.name()); - } - - public static String uriEncode(String value, String enc) { + public static String uriEncodePath(String path) { try { - return URLEncoder.encode(value, enc).replace("+", "%20"); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException(String.format("uriEncode failed, value=\"%s\", enc=\"%s\".", value, enc), e); + URI uri = new URI(null, null, path, null); + return uri.toASCIIString(); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(String.format("uriEncode failed, path=\"%s\".", path), e); } } - public static String uriDecode(String value) { - return uriDecode(value, StandardCharsets.UTF_8.name()); - } + public static String uriDecodePath(String path) { + if (path == null) { + return null; + } - public static String uriDecode(String value, String enc) { try { - return URLDecoder.decode(value, enc); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException(String.format("uriDecode failed, value=\"%s\", enc=\"%s\".", value, enc), e); + return new URI(path).getPath(); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(String.format("uriDecode failed, path=\"%s\".", path), e); } } + /** + * only used by SDK to download from serviceComb producer<br> + * no need to check rtf6266's "filename*" rule. + */ public static String parseFileNameFromHeaderValue(String headerValue) { String fileName = parseParamFromHeaderValue(headerValue, "filename"); fileName = StringUtils.isEmpty(fileName) ? "default" : fileName; - fileName = uriDecode(fileName); + fileName = uriDecodePath(fileName); return new File(fileName).getName(); } } diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpUtils.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpUtils.java index 18f8906cf..5fbd603c0 100644 --- a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpUtils.java +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpUtils.java @@ -16,7 +16,7 @@ */ package org.apache.servicecomb.foundation.common.http; -import java.io.UnsupportedEncodingException; +import java.net.URISyntaxException; import org.hamcrest.Matchers; import org.junit.Assert; @@ -59,36 +59,46 @@ public void parseParamFromHeaderValue_keyNotFound() { Assert.assertNull(HttpUtils.parseParamFromHeaderValue("xx;k=", "kk")); } + @Test + public void uriEncode_null() { + Assert.assertEquals("", HttpUtils.uriEncodePath(null)); + } + + @Test + public void uriDecode_null() { + Assert.assertNull(HttpUtils.uriDecodePath(null)); + } + @Test public void uriEncode_chineseAndSpace() { - String encoded = HttpUtils.uriEncode("测 试"); + String encoded = HttpUtils.uriEncodePath("测 试"); Assert.assertEquals("%E6%B5%8B%20%E8%AF%95", encoded); - Assert.assertEquals("测 试", HttpUtils.uriDecode(encoded)); + Assert.assertEquals("测 试", HttpUtils.uriDecodePath(encoded)); } @Test public void uriEncode_failed() { - expectedException.expect(IllegalStateException.class); - expectedException.expectMessage(Matchers.is("uriEncode failed, value=\"\", enc=\"notExistEnc\".")); - expectedException.expectCause(Matchers.instanceOf(UnsupportedEncodingException.class)); + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage(Matchers.is("uriEncode failed, path=\":\".")); + expectedException.expectCause(Matchers.instanceOf(URISyntaxException.class)); - HttpUtils.uriEncode("", "notExistEnc"); + HttpUtils.uriEncodePath(":"); } @Test public void uriDecode_failed() { - expectedException.expect(IllegalStateException.class); + expectedException.expect(IllegalArgumentException.class); expectedException - .expectMessage(Matchers.is("uriDecode failed, value=\"%E6%B5%8B%20%E8%AF%95\", enc=\"notExistEnc\".")); - expectedException.expectCause(Matchers.instanceOf(UnsupportedEncodingException.class)); + .expectMessage(Matchers.is("uriDecode failed, path=\":\".")); + expectedException.expectCause(Matchers.instanceOf(URISyntaxException.class)); - HttpUtils.uriDecode("%E6%B5%8B%20%E8%AF%95", "notExistEnc"); + HttpUtils.uriDecodePath(":"); } @Test public void parseFileNameFromHeaderValue() { String fileName = "测 试.txt"; - String encoded = HttpUtils.uriEncode(fileName); + String encoded = HttpUtils.uriEncodePath(fileName); Assert.assertEquals(fileName, HttpUtils.parseFileNameFromHeaderValue("xx;filename=" + encoded)); } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java index 8f55886a6..ed776093e 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java @@ -29,6 +29,7 @@ import org.apache.commons.io.IOUtils; import org.apache.servicecomb.foundation.common.http.HttpStatus; +import org.apache.servicecomb.foundation.common.http.HttpUtils; import org.apache.servicecomb.foundation.common.part.FilePartForSend; import org.apache.servicecomb.foundation.vertx.stream.InputStreamToReadStream; import org.slf4j.Logger; @@ -170,8 +171,11 @@ protected void prepareSendPartHeader(Part part) { } if (!serverResponse.headers().contains(HttpHeaders.CONTENT_DISPOSITION)) { + // to support chinese and space filename in firefox + // must use "filename*", (https://tools.ietf.org/html/rtf6266) + String encodedFileName = HttpUtils.uriEncodePath(part.getSubmittedFileName()); serverResponse.putHeader(HttpHeaders.CONTENT_DISPOSITION, - "attachment;filename=" + part.getSubmittedFileName()); + "attachment;filename=" + encodedFileName + ";filename*=utf-8''" + encodedFileName); } } diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerResponseToHttpServletResponse.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerResponseToHttpServletResponse.java index f66dd1b7e..6afd96495 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerResponseToHttpServletResponse.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerResponseToHttpServletResponse.java @@ -304,14 +304,16 @@ public void prepareSendPartHeader_update(@Mocked Part part) { part.getContentType(); result = "type"; part.getSubmittedFileName(); - result = "name"; + result = "测 试"; } }; response.prepareSendPartHeader(part); Assert.assertTrue(serverResponse.isChunked()); Assert.assertEquals("type", response.getHeader(HttpHeaders.CONTENT_TYPE)); - Assert.assertEquals("attachment;filename=name", response.getHeader(HttpHeaders.CONTENT_DISPOSITION)); + Assert.assertEquals( + "attachment;filename=%E6%B5%8B%20%20%20%20%20%E8%AF%95;filename*=utf-8''%E6%B5%8B%20%20%20%20%20%E8%AF%95", + response.getHeader(HttpHeaders.CONTENT_DISPOSITION)); } @Test ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org With regards, Apache Git Services