Java 中的 Http 客户端 API:管理文件

createh51个月前 (02-01)技术教程17

.

概述

本文的目的是展示如何使用 Http Client API 从 REST 端点上传/下载文件内容。

让我们回顾一下调用 Web 资源的步骤,因为此过程与上传/下载文件数据相同。

  • 如果需要,将创建 HttpClient 对象并进一步配置(超时、身份验证器、http 版本、followRedirects...)
  • Http 请求是通过 URL 创建的。可以选择设置其他功能。例如,http 方法、http 标头、超时和 http 版本。
  • 然后,HttpResponse 不是直接创建的,而是作为使用 HttpClient 发送 HttpRequest 的结果返回的。完成后,可以从中检查状态代码和正文(如果有)。

现在,让我们继续下载文件用例。

下载文件内容

我们将按照上述步骤来执行此操作。但首先,让我们看看后端。端点以字节数组的形式发回文件内容

@RestController
@RequestMapping("api/v1/documents")
public class DocumentController {
// ommited code here

    @GetMapping(value = "/{documentId}")
    ResponseEntity downloadDocument(@PathVariable Long 
                                              documentId) {
        Optional document = documentRepo.findById(documentId);

        if (document.isEmpty())
            return ResponseEntity.notFound().build();

        return ResponseEntity
            .ok() 
            .contentType(MediaType.parseMediaType(
                document.get().getType()))
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; 
                filename=\"" + document.get().getName() + "\"")
            .body(new ByteArrayResource( 
                 document.get().getContents()));
    }


API 提供了 BodyHandlers 类(HttpResponse 内部静态类)来管理常见的响应类型,例如 String 或 File。以下是此类中可用的一些有用方法的列表:

  1. ofByteArray:主体写入字节数组。
  2. ofFile:正文写入文件,HttpResponse.body() 返回对其 Path 的引用。
  3. ofFileDownload:主体完全写入文件,并且 HttpResponse.body() 返回文件的 Path 对象。
  4. ofString:正文写入字符串并使用 Content-Type 响应标头中指定的字符集进行解码。
  5. ofInputStream:返回一个输入流,可以在接收到正文时从中读取正文。
  6. ofLines:返回一个 Stream。尸体可能还没有收到。
  7. ofPublisher:返回一个发布者,在接收到正文响应字节时可以从中获取它们。发布者只能而且必须订阅一次。

由于端点返回一个字节数组,因此 ofByteArray 方法似乎是一个很好的匹配。文件名是从 Content-Disposition 标头中提取的。代码片段如下所示:

// Byte Array
HttpResponse response = client
    .send(request, HttpResponse.BodyHandlers.ofByteArray()); 
String headerContentDisposition = 
    (String)response.headers().firstValue("content- 
    disposition").get();
String fileName = 
    "C:/workspace/files/"+getFileName(headerContentDisposition);

// Save to file
Files.write(Paths.get(fileName),
    response.body(), StandardOpenOption.CREATE_NEW);


执行程序后,文件将成功下载到指定位置。

Directory: C:\workspace\files

Mode            LastWriteTime         Length Name                                                                                                                                 
----            -------------         ------ ----                                                                                                                                 
-a----          4/1/2023   7:28 PM    388374 LinuxCommands.jpg   


可以使用不同的 BodyHandler 来下载文件,如以下几行所示

HttpResponse response = client.send(request,                
    HttpResponse.BodyHandlers.ofFile( 
    Path.of("C:/workspace/files/DownloadedFile.jpg")));


另一种选择是使用 ofInputStream 方法。

HttpResponse response = client
   .send(request,             
         HttpResponse.BodyHandlers.ofInputStream());
String headerContentDisposition = 
   (String)response.headers().firstValue("content- 
   disposition").get();
String fileName = "C:/workspace/files/"+ 
   getFileName(headerContentDisposition);

Files.copy(response.body(), Paths.get(fileName), 
   StandardCopyOption.REPLACE_EXISTING);


正如您所看到的,有很多选择可以完成工作:-)。

上传文件内容

在此用户案例中,已设置两个不同的端点来上传文件。因此,我们将看到不同的方式来使身体发挥作用。
请求正文通过提供给 POST 或 PUT 方法之一的 BodyPublisher 提供。BodyPublishers 类提供了许多常见发布者的实现。以下是 BodyPublishers 中可用的一些方法的列表

  1. ofString:返回一个 BodyPublisher,其正文是使用 UTF_8 字符转换的给定字符串。
  2. ofInputStream:从输入流读取数据的主体发布者。
  3. ofByteArray:返回一个请求主体发布者,其主体是给定的字节数组。
  4. ofFile:请求主体发布者,从文件内容中获取数据。
  5. ofByteArrays:请求主体发布者,从字节数组的 Iterable 中获取数据。

现在我们可以继续第一个上传示例。其余端点期望文件内容为字符串。文件类型和名称位于标头中。

@PostMapping()
ResponseEntity uploadDocument(@RequestBody String data, 
                     @RequestHeader("Content-Type") String type,
                     @RequestHeader("fileName") String fileName) {
    Document document = new Document();
    document.setName(fileName);
    document.setCreationDate(LocalDate.now());
    document.setType(type);
    document.setContents(data.getBytes());

    documentRepo.save(document);

    return ResponseEntity.created(URI.create( 
        "http://localhost:8080/api/v1/documents/"+document.getId() 
        )).build();
    } 


如您所见,BodyPublishers.ofFile 方法采用 Path 作为参数。发送请求时,正文将包含文件的内容。

Path file = Paths.get("C:/workspace/files/trees.jpg");
var request = HttpRequest.newBuilder()
    .uri(URI.create("http://localhost:8080/api/v1/documents"))
    .header("Content-Type", "image/jpg")
    .header("fileName", file.getFileName().toString())
    .header("Authorization", 
         getBasicAuthenticationHeader("admin1234","password5678"))
    .POST(HttpRequest.BodyPublishers.ofFile(file))
    .build();

HttpResponse response = client.send(request, 
    HttpResponse.BodyHandlers.ofString());
System.out.printf("Status %s \n", response.statusCode());
System.out.printf("Headers %s \n", 
    response.headers().firstValue("location"));


输出

Status 201 
Headers Optional[http://localhost:8080/api/v1/documents/3] 


文件被保存到数据库中,并且 URL 会在位置标头中返回以查找它。
在第二个示例中,控制器将主体视为字节数组。

@RestController
@RequestMapping("api/v2/documents")
public class DocumentControllerV2 {

    @PostMapping()
    ResponseEntity uploadDocument(@RequestBody byte[] data
            ,@RequestHeader("Content-Type") String type
            ,@RequestHeader("fileName") String fileName) {
    // omitted code
    }
}


ByteArray 方法非常适合这种情况。所需要做的就是将文件的内容转换为字节数组。为此,FileInputStream 将完成这项工作。

String fileName = "C:/workspace/files/java_tutorial.pdf";
var request = HttpRequest.newBuilder()
    .uri(URI.create("http://localhost:8080/api/v2/documents"))
    .header("Content-Type", "image/png")
    .header("filename", 
        fileName.substring(fileName.lastIndexOf("/")))
    .POST(HttpRequest.BodyPublishers.ofByteArray(new 
        FileInputStream(fileName).readAllBytes()))
    .build();

var response = client.send(request, 
    HttpResponse.BodyHandlers.ofString());
System.out.printf("Status %s \n", response.statusCode());
System.out.printf("Headers %s \n", 
    response.headers().firstValue("location"));


输出

Status 201 
Headers Optional[http://localhost:8080/api/v1/documents/4] 


最后,为了验证新文件是否存储在数据库中,将调用列表文件端点。

[
  {
    "name": "PlainText.txt",
    "creationDate": "2023-03-15",
    "size": 26,
    "type": "text/plain"
  },
  {
    "name": "LinuxCommands.jpg",
    "creationDate": "2023-02-07",
    "size": 388374,
    "type": "image/jpeg"
  },
  {
    "name": "trees.jpg",
    "creationDate": "2023-04-02",
    "size": 21011905,
    "type": "image/jpg"
  },
  {
    "name": "/java_tutorial.pdf",
    "creationDate": "2023-04-02",
    "size": 1012786,
    "type": "image/png"
  }


概括

就是这样了。使用 Java 的核心 API 下载和上传文件非常简单。我们必须选择一个合适的正文发布者或正文处理程序来分别发送文件内容和接收文件内容。

关注并回复1即可领取【Java学习资料大礼包】

相关文章

java读写xml文件(java读取xml工具类)

1.读取xml文件文件格式如下: 张无忌 男 光明顶 minmin 3 4...

java进行后台数据写入word模板再进行压缩包下载

经常会遇到这样需求,写一个导出功能,进行word模板格式进行导出,数据库相应数据回填到word模板中,再对这个模板进行导出功能。如果一次性需要导出多个模板情况下,就要对多个进行压缩成一个压缩包再一次性...

java实现文件上传到服务器(java实现文件上传的三种方式)

java实现文件上传到服务器,java实现大文件上传,java实现大文件分块上传,java实现大文件分片上传,java实现大文件切片上传,java实现大文件批量上传,java实现大文件加密上传,jav...

Java高级特性——注解:注解实现Excel导出功能

注解是 Java 的一个高级特性,Spring 更是以注解为基础,发展出一套“注解驱动编程”。这听起来高大上,但毕竟是框架的事,我们也能用好注解吗?的确,我们很少有机会自己写注解,导致我们搞不清楚注解...

「Java进阶」I/O操作必备知识点:字节流读写实例讲解

序欢迎来到全网最完整的Java进阶知识系列教程!!!每天定时更新!!!本期的课程,我们继续分享I/O文件读写。IO文件读写,根据数据格式不同,分为字节流读写和字符流读写,我们今天先讲字节流读写。如果你...

一文了解 DataLeap 中的 Notebook

一、概述Notebook 是一种支持 REPL 模式的开发环境。所谓「REPL」,即「读取-求值-输出」循环:输入一段代码,立刻得到相应的结果,并继续等待下一次输入。它通常使得探索性的开发和调试更加便...