使用RestTemplate在线文件上传到远程接口的正确姿势

有一个业务需求。需要将一个在线文件,在不经过本地存储转换的方式下同步到现有服务中。最终采纳的解决方案是使用Spring提供的RestTemplate来远程操作http服务,实现在线的文件同步上传。

以下是文件上传的核心代码

错误演示


    try{
            MultiValueMap<String, Object> postParameters = new LinkedMultiValueMap<>();
            postParameters.add("type",type);
            postParameters.add("subType",subType);
		 
            //获取文件名称
            URL openUrl = new URL(chenvaFilePath+fileUrl);
            URLConnection urlConnection = openUrl.openConnection();
            int fileLength = urlConnection.getContentLength();
            byte[]bytes = new byte[fileLength];
            // 读取流信息,一次性写入字节数组(与下面正确示例中不同之处)
            InputStream inputStream = urlConnection.getInputStream();
            inputStream.read(bytes);
            inputStream.close();

            HttpHeaders fileHeader = new HttpHeaders();
            fileHeader.setContentType(MediaType.parseMediaType(urlConnection.getContentType()));
            fileHeader.setContentDispositionFormData("upload", fileName);
            HttpEntity<ByteArrayResource> filePart = new HttpEntity<>(new
                    ByteArrayResource(bytes),fileHeader);
            postParameters.add("upload",filePart);
        } catch (Exception e) {
            updateSyncStatus(projectId,dictId,"4");
            throw new RuntimeException("文件上传错误");
        }


        HttpHeaders headers = new HttpHeaders();

        // 使用客户端的请求头,发起请求
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        headers.add("Cookie", newCookie);
        headers.add("User-Agent","Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36");
        HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity(postParameters, headers);
        restTemplate.setRequestFactory(new NoRedirectClientHttpRequestFactory());

        restTemplate.postForObject(UPLOAD_PATH, request, String.class) ;

问题描述

??????在使用上方代码实现文件上传时,是能够上传成功的。在进行文件下载时,出现错误。本业务程序中上传的是pdf文档,下载时文件大小都正确,内容是空白的。最后使用了ByteOutputStream进行缓存字节来实现的。可以正确下载。

正确示例


     try{
            MultiValueMap<String, Object> postParameters = new LinkedMultiValueMap<>();
            postParameters.add("type",type);
            postParameters.add("subType",subType);
            //获取文件名称
            URL openUrl = new URL(chenvaFilePath+fileUrl);
            URLConnection urlConnection = openUrl.openConnection();
            InputStream inputStream = openUrl.openStream();
                
            // 将读取到的字节缓存到此处,用到的使用直接一次性获取
            ByteOutputStream byteOutputStream = new ByteOutputStream();

            int index = -1;
            byte[] bytes = new byte[1024];
            while ((index = inputStream.read(bytes)) != -1) {
                byteOutputStream.write(bytes,0,index);
            }
            inputStream.close();

            HttpHeaders fileHeader = new HttpHeaders();
            fileHeader.setContentType(MediaType.parseMediaType(urlConnection.getContentType()));
            fileHeader.setContentDispositionFormData("upload", fileName);
            HttpEntity<ByteArrayResource> filePart = new HttpEntity<>(new
                    ByteArrayResource(byteOutputStream.getBytes()),fileHeader);
            postParameters.add("upload",filePart);
        } catch (Exception e) {
            throw new RuntimeException("文件上传错误");
        }

        HttpHeaders headers = new HttpHeaders();

        // 使用客户端的请求头,发起请求
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        headers.add("Cookie", newCookie);
        headers.add("User-Agent","Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36");
        HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity(postParameters, headers);
        restTemplate.setRequestFactory(new NoRedirectClientHttpRequestFactory());

        restTemplate.postForObject(UPLOAD_PATH, request, String.class) ;

实现SimpleClientHttpRequestFactory类

public class NoRedirectClientHttpRequestFactory extends SimpleClientHttpRequestFactory {

    @Override
    protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
        // TODO Auto-generated method stub
        super.prepareConnection(connection, httpMethod);
        // 禁止自动重定向
        connection.setFollowRedirects(false);
    }
}

????至此文件可以正常上传

问题复现

????在使用maven进行打包编译时,提示错误,错误信息如下:

com.sun.xml.internal.messaging.saaj.util程序包不存在

????于是乎开始了网上冲浪,在上线的紧要关头怎么能出现问题呢是吧。
????原因是在上面示例程序中使用了ByteOutputStream类,导致maven进行compiler的时候找不到该类信息,在maven编译打包时指定该类位置信息即可。

          <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin.version}</version>
                <configuration>
                   <!--重要部分-->
                    <compilerArguments>
                        <bootclasspath>${env.JAVA_HOME}\jre\lib\rt.jar;${env.JAVA_HOME}\jre\lib\jce.jar</bootclasspath>
                    </compilerArguments>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${maven.compiler.encoding}</encoding>
                    <verbose/>
                </configuration>
            </plugin>

????其中${env.JAVA_HOME}是java的系统变量,在maven中可以直接使用。使用mvn help:system可以系统的查看内置信息。

????还有一些maven的常用内置变量如下:

${basedir} 项目根目录
${project.build.directory} 构建目录,缺省为target
${project.build.outputDirectory} 构建过程输出目录,缺省为target/classes
${project.build.finalName} 产出物名称,缺省为${project.artifactId}-${project.version} 当前版本
${project.packaging} 打包类型,缺省为jar

使用RestTemplate在线文件上传到远程接口的正确姿势

上一篇:myabtis封装的mapper


下一篇:做为 iOS 开发者 现在对未来迷茫怎么办?