无法使用 Rest 模板下载由 REST API 生成的 zip 文件

问题描述

我正在尝试下载由我编写的 REST API 生成的 zip 文件(此 REST API 工作正常,我可以从邮递员那里下载 zip 文件)。

从 zip 文件生成 REST API 中提取代码

@GetMapping(value = "/zipDownloadGraphQLSchema",produces = "application/zip")
    public void zipDownloadGraphQLSchema(@RequestBody SchemaGeneratorInput schemaGeneratorInput,HttpServletResponse response) {
        long currentRequestTimeInMillis = System.currentTimeMillis();
        String fileBasePath = service.generateGraphQLSchemaFiles(schemaGeneratorInput,currentRequestTimeInMillis);
        fileBasePath += "/";
        File directoryPath = new File(fileBasePath);
        String[] files = directoryPath.list();
        try {
            ZipOutputStream zipOut = new ZipOutputStream(response.getoutputStream());
            for (String fileName : Objects.requireNonNull(files)) {
                FileSystemResource resource = new FileSystemResource(directoryPath + "/" + fileName);
                ZipEntry zipEntry = new ZipEntry(Objects.requireNonNull(resource.getFilename()));
                zipEntry.setSize(resource.contentLength());
                zipOut.putNextEntry(zipEntry);
                StreamUtils.copy(resource.getInputStream(),zipOut);
                zipOut.closeEntry();
            }
            zipOut.finish();
            zipOut.close();
            response.setContentType("application/force-download");
            response.setStatus(HttpServletResponse.SC_OK);
            response.addheader(HttpHeaders.CONTENT_disPOSITION,"attachment; filename=\"GraphQLSchema.zip\"");

        } catch (Exception e) {
            log.error(e.getMessage());
            throw new BusinessException(HttpStatus.INTERNAL_SERVER_ERROR.value(),Constants.ERROR_PROCESSING_REQUEST);
        } finally {
            try {
                FileUtils.deleteDirectory(new File(fileBasePath));
            } catch (IOException e) {
                log.error(e.getMessage());
                // throw new BusinessException(HttpStatus.INTERNAL_SERVER_ERROR.value(),Constants.ERROR_DELETING_USER_FOLDER);
            }
        }
    }

基于以下几个解决方案,我尝试了 2 种编写 客户端方法

方法 1(非流式传输):

public ResponseEntity<String> callGraphQlFileSGen1(GraphQlFileGenRequest request,String correlationId) throws IOException {
    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM));
    //headers.setAccept(Arrays.asList(MediaType.ALL));
    //headers.add("Content-Type","application/zip");
    headers.setContentType(MediaType.APPLICATION_JSON);
    httpentity<GraphQlFileGenRequest> entity = new httpentity<>(request,headers);
    ResponseEntity<byte[]> response = null;
    try {
        response = restTemplate.exchange(graphQlFileGenPath,HttpMethod.GET,entity,byte[].class,"1");
    } catch (Exception e) {
        LOGGER.error("Failed to call graphQlApiGen. {}",e.toString());
        throw e;
    }
    if (response.getStatusCode().equals(HttpStatus.OK)) {
        try {
            FileOutputStream output = new FileOutputStream(
                    new File(graphQlFileDownloadpath + "/GraphQlSchema.zip"));
            IoUtils.write(response.getBody(),output);
        } catch (IOException e) {
            LOGGER.error("Unable to write graphQl file. {}",e.toString());
            e.printstacktrace();
            throw e;
        }

    }
    return ResponseEntity.status(HttpStatus.OK).headers(headers).body("{}");
}

我常用的 RestTemplate 代码

@Bean("mtlsRestTemplate")
    public RestTemplate getMtlsRestTemplate() throws Exception {

        Resource resource = resourceLoader.getResource("classpath:" + mtlsKeystorePath);
        KeyStore keystore = KeyStore.getInstance("jks");
        InputStream inputStream = null;
        try {
            inputStream = resource.getInputStream();
            keystore.load(inputStream,mtlsKeyStorePassword.tochararray());
        } finally {
            if (null != inputStream) {
                inputStream.close();
            }
        }

        SSLContext sslContext = new SSLContextBuilder().loadKeyMaterial(keystore,mtlsKeyStorePassword.tochararray())
                .build();
        SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);
        HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
        RestTemplate restTemplate = new RestTemplate(factory);
        List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
        
        messageConverters.add(new ByteArrayHttpMessageConverter());
        messageConverters.add(new MappingJackson2HttpMessageConverter());
        messageConverters.add(new StringHttpMessageConverter());
        messageConverters.add(new ResourceHttpMessageConverter());
        messageConverters.add(new AllEncompassingFormHttpMessageConverter());
        // Add the message converters to the restTemplate
        restTemplate.setMessageConverters(messageConverters);
        return restTemplate;
    }

方法 2(流媒体):

public ResponseEntity<String> callGraphQlFileSGen2(GraphQlFileGenRequest request,String correlationId)
            throws JsonProcessingException {
        HttpHeaders dummyHeaders = new HttpHeaders();
        ObjectMapper objectMapper = new ObjectMapper();
        BodySettingRequestCallback requestCallBack = null;
        try {
            requestCallBack = new BodySettingRequestCallback(objectMapper.writeValueAsstring(request),objectMapper);
        } catch (JsonProcessingException e1) {
            LOGGER.error("Couldn't write value as string for request. {}",e1.toString());
            e1.printstacktrace();
            throw e1;
        }

        // Streams the response instead of loading it all in memory
        ResponseExtractor<Void> responseExtractor = response -> {
            // Response written to a file
            Path path = Paths.get(graphQlFileDownloadpath + "/GraphQlSchema.zip");
            Files.copy(response.getBody(),path);
            return null;
        };

        try {
            restTemplate.execute(URI.create(graphQlFileGenPath),requestCallBack,responseExtractor);
        } catch (Exception e) {
            LOGGER.error("Error downloading GraphQl file. {}",e.toString());
            e.printstacktrace();
            throw e;
        }
        return ResponseEntity.status(HttpStatus.OK).headers(dummyHeaders ).body("{}");

    }

BodySettingRequestCallback.class

public class BodySettingRequestCallback implements RequestCallback {

    private String body;
    private ObjectMapper objectMapper;

    public BodySettingRequestCallback(String body,ObjectMapper objectMapper) {
        this.body = body;
        this.objectMapper = objectMapper;
    }

    @Override
    public void doWithRequest(ClientHttpRequest request) throws IOException {
        request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM,MediaType.ALL));
        byte[] json = getEventBytes();
        request.getBody().write(json);
    }

    byte[] getEventBytes() throws JsonProcessingException {
        return objectMapper.writeValueAsBytes(body);
    }
}

对于 Approach-1,我收到以下错误

org.springframework.web.client.HttpClientErrorException$NotAcceptable: 406 : [no body]

对于 Approach-2 我收到以下错误

org.springframework.web.client.HttpClientErrorException$BadRequest: 400 : [no body]

过去 1 天我一直被这个问题困扰。对此的任何解决方案/建议将不胜感激。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)