之前的文章写了 Android 运用 NanoHTTPD 上传和下载文件,当时测试时用的是 curl 命令上传下载文件,现在要使用 Spring 后端来上传和下载文件到 Android 端,服务器本地文件 <-------> Spring <-------> Android

本文参考:Uploading MultipartFile with Spring RestTemplate 特别是它的 Github 代码 MultipartFileUploadClient.java

上传文件

创建一个类 MultipartFileUpload

import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
@Service
public class MultipartFileUpload {

    /**
     * 上传单个文件
     * @param filePath 要上传的文件的本地绝对路径
     * @param serverUrl 接收上传文件的远端 URL
     */
    public void uploadSingleFile(String filePath,String serverUrl) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
        body.add("file",new FileSystemResource(filePath));

        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> response = restTemplate.postForEntity(serverUrl, requestEntity, String.class);
        System.out.println("Response code: " + response.getStatusCode());
    }

    /**
     * 上传多个文件
     * @param filePaths 要上传的多个文件的绝对路径 String[] 数组
     * @param serverUrl 接收上传文件的远端 URL
     */
    public void uploadMultipleFile(String[] filePaths,String serverUrl) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
        for(String filePath : filePaths) {
            body.add("file",new FileSystemResource(filePath));
        }

        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> response = restTemplate.postForEntity(serverUrl, requestEntity, String.class);
        System.out.println("Response code: " + response.getStatusCode());
    }
}

HttpHeaders 是 HTTP 请求头对象,用于设置 HTTP 请求头部。

headers.setContentType(MediaType.MULTIPART_FORM_DATA); 是将请求设置为表单上传。

FileSystemResource 是 Spring 中以文件系统的绝对路径的方式访问静态资源。

MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file",new FileSystemResource(filePath));

在请求主体,也就是表单中添加要上传的文件。

HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.postForEntity(serverUrl, requestEntity, String.class);

创建 HTTP 请求实体,并使用 RestTemplate 的 postForEntity 方法将其发出。

下载文件

下载文件就比较简单一点了,因为我们在之前的 Android 中对于下载文件,只要请求 http://ip:port/?download=xxxxxx就代表要下载的文件。具体可以看看下面的 Android 代码

// HTTP GET
if (session.getMethod() == Method.GET) {
    // 要下载的文件名称
    String fileName = Objects.requireNonNull(session.getParameters().get("download")).get(0);
    Log.d("fileName",fileName);
    if(!fileName.isEmpty()) {
        File downloadFile;
        downloadFile = new File(PathUtils.getExternalDownloadsPath() + "/" + fileName);
        return downloadFile(downloadFile);
    }
}

在 Spring 中创建 FileDownload

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.io.FileOutputStream;
import java.io.IOException;

@Service
public class FileDownload {
    public void download(String fileName,String newFilePath) throws IOException {
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        String serverUrl = "http://ip:port/?download=";
        ResponseEntity<byte[]> response
                = restTemplate.exchange(serverUrl + fileName, HttpMethod.GET,new HttpEntity<>(headers),byte[].class);
        byte[] body = response.getBody();
        try (FileOutputStream fos = new FileOutputStream(newFilePath)) {
            fos.write(body);
        }
    }
}

serverUrl 代表要下载的文件服务器地址。

ResponseEntity<byte[]> response = restTemplate.exchange(serverUrl + fileName, HttpMethod.GET,new HttpEntity<>(headers),byte[].class); 这里构造了一个字节数组的 HTTP 响应,因为在 Android 的下载文件代码中,文件下载的响应也是 private Response downloadFile(File file),所以将响应强制类型转换成了 byte[]

byte[] body = response.getBody();
try (FileOutputStream fos = new FileOutputStream(newFilePath)) {
    fos.write(body);
}

将得到的字节数组写到文件,FileOutputStream()形参需要绝对路径地址,另外有个小 BUG ,如果 Android 中待下载的文件是没有内容的(0字节),下载到本地会抛出 byte[] 为空的异常。


本文其他参考:
Multipart File Upload Using Spring Rest Template + Spring Web MVC

How to POST form data with Spring RestTemplate?

byte[] to file in Java

RestTemplate发送get和post请求,下载文件

标签: none

评论已关闭