【Java】三种实现网络通讯的方式以及相关协议的使用示例

Java网络 - 应用篇


👾以下代码均经过本人实测,请放心食用。顺便求个关注,谢谢!!

文章目录

  • Java网络 - 应用篇
    • Socket 篇
      • 简介
      • 代码实现
        • SockerServer
        • SocketClient
    • RestTemplate 篇
      • 简介
      • ...ForEntity 与 ...ForObject 对比
      • 示例 1 | 发送 Get 请求
      • 示例 2 | 发送 Post 请求
      • 示例 3 | 发送 Put 请求
      • 示例 4 | 发送 Delete 请求
      • 示例 5 | 发送携带 Params 的请求
      • 示例 6 | 上传文件
      • 示例 7 | 文件下载接口
      • 使用 exchange 模拟各种请求
        • 模拟 GET 请求
        • 模拟 Post 请求
        • 模拟 PUT 请求
        • 模拟 Delete 请求
    • OpenFeign 篇
      • 简介
      • 示例 1 | 发送 Get 请求
      • 示例 2 | 发送 Post 请求
      • 示例 3 | 发送 Put 请求
      • 示例 4 | 发送 Delete 请求
      • 示例 5 | 接收多个Params
      • 示例 6 | 上传文件
      • 示例 7 | 下载文件示例
      • 示例 8 | 携带Http请求头发送报文
  • 附录
    • 附录 1 | 接收 Get 请求
    • 附录 2 | 接收 Post 请求
    • 附录 3 | 接收 Put 请求
    • 附录 4 | 接收 Delete 请求
    • 附录 5 | 接收多个 Params
    • 附录 6 | 上传文件接口
    • 附录 7 | 下载文件接口
    • 附录 8 | 接收 Post 请求且接受 HttpHeaders
    • 附录 9 | 接收 Put 请求
  • 结语

Socket 篇

简介

Socket是对于TCP协议的封装,在使用Socket的时候,我们需要在服务端创建一个Socket套接字(也就是一个端口),此时我们可以使用Socket客户端请求该IP:端口,就可以访问到Socket套接字。

优点:比较灵活,不局限于http请求,可以发送任何形式的内容,比如JSON、XML、流等,也可以模拟http请求(但不建议)

缺点:使用上的灵活往往意味着开发上更为负责,一般我们需要自己封装一些逻辑才能在项目中更好的使用

代码实现

下面简单实现一段代码,来实现Socket服务端,启动这段代码后,代码会进入到阻塞状态,直到接受到客户端的信息为止。

SockerServer
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;/*** @author Jim* @date 2024/06/15*/
public class SocketServer {public static void main(String[] args) {int port = 12345;try {// 创建 ServerSocket 对象ServerSocket serverSocket = new ServerSocket(port);System.out.println("服务器启动,等待客户端连接...");// 监听客户端的连接请求Socket clientSocket = serverSocket.accept();System.out.println("客户端已连接");// 获取输入流,接收客户端消息InputStream input = clientSocket.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(input));String message = reader.readLine();System.out.println("客户端消息:" + message);// 接收完毕,获取输出流,向客户端发送消息OutputStream output = clientSocket.getOutputStream();String result = "你好,我是服务端!";System.out.println("给客户端返回信息:"+result);output.write(result.getBytes());// 关闭流和连接input.close();output.close();clientSocket.close();serverSocket.close();} catch (IOException e) {e.printStackTrace();}}
}
SocketClient
import java.io.*;
import java.net.Socket;/*** @author Jim* @date 2024/06/15*/
public class SocketClient {public static void main(String[] args) {String serverAddress = "localhost";int port = 12345;try {// 创建 Socket 对象,连接到服务器Socket socket = new Socket(serverAddress, port);System.out.println("连接到服务器");// 获取输出流,向服务器发送消息OutputStream output = socket.getOutputStream();String message = "Jim.kk\n";System.out.println("向服务端发送:"+message);output.write(message.getBytes());// 获取服务端消息InputStream input = socket.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(input));String result = reader.readLine();System.out.println("服务器消息: " + result);// 关闭流和连接output.close();input.close();socket.close();} catch (IOException e) {e.printStackTrace();}}
}

RestTemplate 篇

简介

RestTemplate 是一种支持RESTful 服务的模板类,它简化了对 HTTP 资源的访问,并提供了一组丰富的方法来处理 HTTP 请求和响应。

  • 支持多种 HTTP 方法GETPOSTPUTDELETEHEADOPTIONSPATCH 等,可以根据需要选择合适的 HTTP 方法进行请求。

  • 处理请求和响应:提供了方法来处理请求体、响应体、请求头和响应头等信息,使得与 RESTful 服务的交互变得简单和灵活。

  • 支持响应数据绑定:可以将响应的 JSON 或 XML 数据绑定到 Java 对象,使用 Jackson 或者 JAXB 等进行序列化和反序列化。

  • 错误处理:支持处理不同的 HTTP 状态码和错误,可以定义错误处理逻辑以及异常处理。

  • HTTP 连接池和请求工厂:内置了 HTTP 连接池和请求工厂,提高了性能并允许对 HTTP 客户端的配置进行优化。

  • 拦截器:可以添加请求和响应的拦截器,用于在发送请求和处理响应之前或之后执行自定义逻辑。

…ForEntity 与 …ForObject 对比

首先两者都可以接收我们在服务端要返回的内容,但是区别在于...ForObject方法仅仅接收服务端返回的内容,而``xxxForEntity方法则会返回一个ResponseEntity<返回内容类型>`的对象,其中包含响应码、响应描述、响应内容(body)以及一个响应头信息

示例 1 | 发送 Get 请求

服务端(接收方)代码见[附录1]

本次示例使用getForEntitygetForObject来发送请求,除说明使用方式外,还可以对比二者之间的区别。

public static void main(String[] args) {// 方法 1 | forEntity 获取完整的返回响应RestTemplate rest = new RestTemplate();ResponseEntity<String> response = rest.getForEntity("http://127.0.0.1:8080/test/get/{id}", String.class,321);System.out.println("forEntity -- " response); // forEntity -- <200,返回ID为321的信息,[Content-Type:"text/plain;charset=UTF-8", Content-Length:"23", Date:"Sun, 16 Jun 2024 16:49:16 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]>// 方法 2 | forObject 仅获取返回内容String result = rest.getForObject("http://127.0.0.1:8080/test/get/{id}", String.class,321);System.out.println("forObject -- " + result); // forObject -- 返回ID为321的信息
}

执行结果:

在这里插入图片描述

示例 2 | 发送 Post 请求

本次请求传递一个User对象,接收端代码User类请见[附录2]

public static void main(String[] args) {User user = new User("Jim.kk","123");RestTemplate rest = new RestTemplate();ResponseEntity<String> response = rest.postForEntity("http://127.0.0.1:8080/test/post",user,String.class);System.out.println("forEntity -- " + response); // forEntity -- <200,接收成功,[Content-Type:"text/plain;charset=UTF-8", Content-Length:"12", Date:"Sun, 16 Jun 2024 17:02:01 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]>String result = rest.postForObject("http://127.0.0.1:8080/test/post",user, String.class);System.out.println("forObject -- " + result); // forObject -- 接收成功
}

执行结果:

在这里插入图片描述

示例 3 | 发送 Put 请求

RestTemplate提供的PUT方法没有返回值信息,除了更新的对象信息以外,还必须包含一个更新的资源ID,该请求没有返回值,但是好在我们可以使用exchange方法来模拟PUT请求并获取返回值信息(后文有介绍)。使用put方法发送请求代码如下。

关于PUT请求的接收接口请见[附录3]。

public static void main(String[] args) {User user = new User("Jim.kk","123");RestTemplate rest = new RestTemplate();rest.put("http://127.0.0.1:8080/test/put/{id}",user,String.class,123);
}

请求结果如下所示:

在这里插入图片描述

示例 4 | 发送 Delete 请求

我们知道Delete在最初被设计出来的时候,仅用于删除服务器上的一个“资源”,因此该方法只有一个地址值与一个资源ID,在服务器端我们需要通过@PthVariable(地址变量值)来接收这个参数,与put方法一样,该请求没有返回值,如果希望访问Delete方法并且得到一个返回值的话,可以使用exchange方法来模拟Delete方法(后文有介绍)。

关于Delete方法的接收端请见[附录4]

public static void main(String[] args) {RestTemplate rest = new RestTemplate();HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);rest.delete("http://localhost:8080/test/delete/{id}",1); // NULL
}

在这里插入图片描述

示例 5 | 发送携带 Params 的请求

在后端接口中,我们经常用到(@RequestParam("xx1") String xx1,.....)的请求,我们知道在浏览器中要是访问该方法,需要再URL后面跟?拼接参数,多个参数之间用&符号连接,在RestTemplate中也是一样的。

关于接收端方法详见[附录5]。

(此处以Get方法做事例)

public static void main(String[] args) throws UnsupportedEncodingException {RestTemplate rest = new RestTemplate();User user = new User("Jim.kk","123");ResponseEntity<String> result = rest.exchange("http://localhost:8080/test/param?username=Jim.kk&password=123", HttpMethod.GET, new HttpEntity<>(user), String.class);System.out.println(result.getBody()); // OKK
}

在这里插入图片描述

示例 6 | 上传文件

关于上传文件接口请见[附录6]。

上传接口会接受一个MultipartFile对象(名为file),一个String对象(名为description描述),因此客户端需要在HttpEntity.body中添加这两个对象,并且上传到服务器即可。

public static void main(String[] args) {// 创建RestTemplate对象RestTemplate restTemplate = new RestTemplate();// 设置文件上传的URLString uploadUrl = "http://localhost:8080/test/upload";// 准备上传的文件File file = new File("/path/to/your/source/wallhaven-l3kq9y.png");FileSystemResource fileSystemResource = new FileSystemResource(file);// 设置请求体,包含文件和其他参数MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();body.add("file", fileSystemResource);body.add("description", "一张图");// 设置请求头部信息HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.MULTIPART_FORM_DATA);// 创建HttpEntity,包含请求头部和请求体HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);// 发送POST请求,上传文件ResponseEntity<String> response = restTemplate.exchange(uploadUrl,HttpMethod.POST,requestEntity,String.class);// 处理响应结果if (response.getStatusCode() == HttpStatus.OK) {System.out.println("文件上传成功,服务器返回:" + response.getBody());} else {System.err.println("文件上传失败,状态码:" + response.getStatusCode());}
}

执行结果:

在这里插入图片描述

文件上传成功后,该图已经被存储在了相应路径中:

在这里插入图片描述

示例 7 | 文件下载接口

关于文件下载接口的事例请看[附录7]。

文件下载接口会返回一个ResponseEntity<Resource>对象,Resource中放了一个使用ByteArrayResource(二进制数组)读取的文件内容。

public static void main(String[] args) {// 创建RestTemplate对象RestTemplate restTemplate = new RestTemplate();// 文件下载的URLString fileUrl = "http://127.0.0.1:8080/test/download";// 发送GET请求,接收文件内容ResponseEntity<Resource> response = restTemplate.exchange(fileUrl,HttpMethod.GET,null,Resource.class);// 处理响应结果if (response.getStatusCode() == HttpStatus.OK) {try {// 获取文件名String fileName = extractFileName(response);// 保存文件到本地saveFileToLocal(response.getBody(), fileName);System.out.println("文件下载成功,保存为:" + fileName);} catch (IOException e) {System.err.println("保存文件失败:" + e.getMessage());}} else {System.err.println("文件下载失败,状态码:" + response.getStatusCode());}
}// 提取文件名
private static String extractFileName(ResponseEntity<Resource> response) {String contentDisposition = response.getHeaders().getFirst(HttpHeaders.CONTENT_DISPOSITION);if (contentDisposition != null && !contentDisposition.isEmpty()) {return contentDisposition.replaceFirst("(?i)^.*filename=\"?([^\"]+)\"?.*$", "$1");}return "downloaded.file";
}// 将文件保存到本地
private static void saveFileToLocal(Resource body, String fileName) throws IOException {String path = "/path/to/save/";byte[] bytes = new byte[body.getInputStream().available()];body.getInputStream().read(bytes);try (FileOutputStream fos = new FileOutputStream(path + fileName)) {fos.write(bytes);}
}

在这里插入图片描述

文件保存结果如下所示:

在这里插入图片描述

使用 exchange 模拟各种请求

上文说到一些方法没有返回值,因此我们无法判断我们的请求是成功还是失败,但是我们可以通过exchange来模拟各种请求,以达到获取返回信息的目的,以下是模拟各种请求的示例代码。

模拟 GET 请求

关于GET请求的接收方代码可以查看[附录1]

    public static void main(String[] args) {RestTemplate restTemplate = new RestTemplate();ResponseEntity<String> response = restTemplate.exchange("http://127.0.0.1:8080/test/get/1",HttpMethod.GET,HttpEntity.EMPTY,String.class);System.out.println(response);}

相应结果如下:

在这里插入图片描述

模拟 Post 请求

在发送Post请求时,除了携带请求体以外,我们还可以发送一个请求头,以下代码中我们携带请求头一起发送。

接收端代码请看[附录8]

public static void main(String[] args) {User user = new User("Jim.kk","123");HttpHeaders headers = new HttpHeaders();headers.add("xxx","testHeaderMsg");RestTemplate restTemplate = new RestTemplate();ResponseEntity<String> response = restTemplate.exchange("http://127.0.0.1:8080/test/post2",HttpMethod.POST,new HttpEntity<>(user,headers),String.class);System.out.println(response);
}

相应结果如下:

在这里插入图片描述

模拟 PUT 请求

本次请求仅传递一个RequestBody对象,接收端代码详见[附录9]

public static void main(String[] args) {User user = new User("Jim.kk","123");RestTemplate restTemplate = new RestTemplate();ResponseEntity<String> response = restTemplate.exchange("http://127.0.0.1:8080/test/put2",HttpMethod.PUT,new HttpEntity<>(user),String.class);System.out.println(response);
}

执行结果为:

在这里插入图片描述

模拟 Delete 请求

关于接收端代码详见[附录4]

public static void main(String[] args) {RestTemplate restTemplate = new RestTemplate();ResponseEntity<String> response = restTemplate.exchange("http://127.0.0.1:8080/test/delete/1",HttpMethod.DELETE,HttpEntity.EMPTY,String.class);System.out.println(response);
}

执行结果为:

在这里插入图片描述

OpenFeign 篇

简介

OpenFeign是在SpringCloud中常用的一个Http协议通讯工具,在普通的项目中也可以使用,它比Template更加高层抽象和声明式,不过也更加的方便使用。

使用OpenFeign就像是将远程的接口在本地做了一个“替身”,每次我们仅需要访问这个“替身”,即可向远程的接口发送数据。此是代码访问可以像访问本地接口一样访问远程接口。

OpenFeign分为客户端与服务端,这里主要介绍客户端,服务端一般只会在SpringCloud项目中使用,在此不做赘述。

使用OpenFeign之前,我们需要在启动类上添加上@EnableFeignClients注解,里面可以填写你的Feign代码所在的包路径,如果可以扫描到的话,不写路径(仅仅写一个注解)也可以。

@SpringBootApplication
@EnableFeignClients("com.jim.springbootdemo.feign")
public class SpringBootDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringBootDemoApplication.class, args);}}

示例 1 | 发送 Get 请求

以访问[附录1]为例,我们在项目下创建feign/TestFeign.java接口,并在类中添加以下代码,其中:

@FeignClient:中要为该接口命名,以及写上接口的URL,此处是IP+端口

@GetMapping("/test/{id}"):表示当前方法用于发送一个Get请求,请求地址为接口上的URL+该注解中的地址

@PathVariable("id"):表示该远程接口存在一个可变地址值参数,我们调用该接口的时候需要传入该参数

@FeignClient(name = "testFeign", url = "http://localhost:8080")
public interface TestFeign {@GetMapping("/get/{id}")String getData(@PathVariable("id") String id);}

如果对比[附录1]中的代码,我们可以发现该抽象方法所使用的所有注解以及参数,都与远程方法一致,这就是OpenFeign的好处,我们仅需要在该接口类中“复制”一个远程接口的“影子”,我们就能直接调用该远程方法。下面的代码中我们调用该方法,以实现访问远程方法。

@RestController
@RequestMapping("/feign")
public class FeignController {@AutowiredTestFeign testFeign;@GetMapping("/get")public String feign1Get() {return testFeign.getData();}
}

执行结果:略

示例 2 | 发送 Post 请求

在同一个接口类中,我们定义一个用于访问[附录2]的Feign接口,如上文所说,我们仅需要拷贝一个远程接口的“影子”即可,代码如下:

@FeignClient(name = "testFeign", url = "http://localhost:8080")
public interface TestFeign {@PostMapping("/test/post")String postData(@RequestBody User user);}

同样,对比[附录2]的接收端接口,会发现该成员方法上的注解与其也是一致的,随后我们也像示例1中一样,调用一下该方法:

@RestController
@RequestMapping("/feign")
public class FeignController {@AutowiredTestFeign testFeign;@GetMapping("/post")public String feign2Post() {User user = new User("Jim.kk","123");testFeign.postData(user);return "OKK";}}

执行结果:略

示例 3 | 发送 Put 请求

与上面一样,创建[附录3]的影子接口,随后访问即可,Feign代码如下:

@FeignClient(name = "testFeign", url = "http://localhost:8080")
public interface TestFeign {@PutMapping("/test/put/{id}")String putData(@PathVariable("id") String id, @RequestBody User user);}

调用如下:

@RestController
@RequestMapping("/feign")
public class FeignController {@AutowiredTestFeign testFeign;@GetMapping("/put")public String feign3Put() {return testFeign.putData("1",new User("Jim.kk","1234"));}}

执行结果:略

示例 4 | 发送 Delete 请求

复制[示例4]的影子接口,代码如下:

@FeignClient(name = "testFeign", url = "http://localhost:8080")
public interface TestFeign {@DeleteMapping("/test/delete/{id}")String deleteData(@PathVariable("id") String id);}

调用代码如下:

@RestController
@RequestMapping("/feign")
public class FeignController {@AutowiredTestFeign testFeign;@GetMapping("/delete")public String feign3Del() {return testFeign.deleteData("1");}}

执行结果:略

示例 5 | 接收多个Params

先拷贝[示例5]的影子接口,随后调用即可,Feign代码如下:

@FeignClient(name = "testFeign", url = "http://localhost:8080")
public interface TestFeign {@GetMapping("/test/param")String sndParams(@RequestParam("username") String username,@RequestParam("password") String password);}

调用代码如下:

@RestController
@RequestMapping("/feign")
public class FeignController {@AutowiredTestFeign testFeign;@GetMapping("/params")public String feign4Params(){return testFeign.sndParams("1","2");}}

示例 6 | 上传文件

一样拷贝[附录6]的影子接口,随后调用,Feign代码如下:

@FeignClient(name = "testFeign", url = "http://localhost:8080")
public interface TestFeign {@PostMapping(value="/test/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)ResponseEntity<String> upload(@RequestPart("file") MultipartFile file,@RequestParam("description") String description);}

请注意:请求头上一定要有consumes = MediaType.MULTIPART_FORM_DATA_VALUE

调用如下:

@GetMapping("/upload")
public ResponseEntity<String> feign6Upload() throws IOException {byte[] bytes = Files.readAllBytes(Paths.get("/Users/hsk/DevCodes/Gitlab/JavaNetworkL/SpringBootDemo/local/feignUpload.png"));MultipartFile file = new MockMultipartFile("file","a.png","image/png",bytes);return testFeign.upload(file,"one image");
}

通过以上方式,就可以向[附录6]上传一个文件

示例 7 | 下载文件示例

Feign调用[附录7]实现文件下载功能的代码如下:

@FeignClient(name = "testFeign", url = "http://localhost:8080")
public interface TestFeign {@GetMapping("/test/download")ResponseEntity<Resource> downloadFile();}

调用方式如下:

@GetMapping("/download")
public String feign7Download() throws IOException {ResponseEntity<Resource> resource = testFeign.downloadFile();Resource body = resource.getBody();FileOutputStream os = new FileOutputStream("/Users/hsk/DevCodes/Gitlab/JavaNetworkL/SpringBootDemo/local/" + extractFileName(resource));byte[] bytes = new byte[body.getInputStream().available()];os.write(bytes);return "OKK";
}private String extractFileName(ResponseEntity<Resource> response) {String contentDisposition = response.getHeaders().getFirst(HttpHeaders.CONTENT_DISPOSITION);if (contentDisposition != null && !contentDisposition.isEmpty()) {return contentDisposition.replaceFirst("(?i)^.*filename=\"?([^\"]+)\"?.*$", "$1");}return "downloaded.file";
}

运行结果:略

示例 8 | 携带Http请求头发送报文

这里以访问[附录8]为例

Feign有两种添加请求头的方式,第一种我们可以像是[示例6]一样,直接在定义feign的时候添加consumes来定义需要携带的请求头,另外我们也可以通过以下代码传递一个HttpHeaders以达到发送请求头的效果。

@FeignClient(name = "testFeign", url = "http://localhost:8080")
public interface TestFeign {@PostMapping("/test/post2")String headers(@RequestHeader HttpHeaders headers, @RequestBody User user);}

调用代码如下:

@GetMapping("/headers")
public String feign8HttpHeaders() {HttpHeaders headers = new HttpHeaders();headers.add("xxx","Jim.kk");headers.add("yyy","Jim.zzZ");return testFeign.headers(headers,new User("Jim.kk","123"));
}

执行结果:略

附录

附录 1 | 接收 Get 请求

此处使用@PathVariable来接收参数。

@RestController
@RequestMapping("/test")
public class TestController {@GetMapping("/get/{id}")public String get(@PathVariable("id") String id) {System.out.println(id);return "返回ID为" + id + "的信息";}}

附录 2 | 接收 Post 请求

@RestController
@RequestMapping("/test")
public class TestController {@PostMapping("/post")public String post(@RequestBody User user) {System.out.println("接收成功:" + user);return "接收成功";}}

User类代码如下:

@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User {private String username;private String password;
}

附录 3 | 接收 Put 请求

@RestController
@RequestMapping("/test")
public class TestController {@PutMapping("/put/{id}")public String put(@PathVariable("id") String id,@RequestBody User user) {return "id为"+id+"的用户更新为"+user;}}

User类代码如下:

@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User {private String username;private String password;
}

附录 4 | 接收 Delete 请求

@RestController
@RequestMapping("/test")
public class TestController {@DeleteMapping("/delete/{id}")public String delete(@PathVariable("id") String id) {System.out.println(id);return "id为" + id + "的资源已删除";}}

附录 5 | 接收多个 Params

@RestController
@RequestMapping("/test")
public class TestController {@GetMapping("/param")public String params(@RequestParam("username") String username,@RequestParam("password") String password) {System.out.println("username:"+username+"password:"+password);return "OKK";}}

附录 6 | 上传文件接口

@RestController
@RequestMapping("/test")
public class TestController {@PostMapping("/upload")public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFile file,@RequestParam("description") String description) {String UPLOAD_DIR = "/path/to/save";// 检查文件是否为空if (file.isEmpty()) {return ResponseEntity.badRequest().body("上传的文件为空");}// 检查并创建上传文件保存目录File uploadDir = new File(UPLOAD_DIR);if (!uploadDir.exists()) {uploadDir.mkdirs();}// 获取文件名String fileName = file.getOriginalFilename();Path filePath = Paths.get(UPLOAD_DIR, fileName);try {// 保存文件到指定目录Files.write(filePath, file.getBytes());} catch (IOException e) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件上传失败:" + e.getMessage());}// 返回上传成功的消息return ResponseEntity.ok("文件上传成功,文件描述:" + description);}}

附录 7 | 下载文件接口

@RestController
@RequestMapping("/test")
public class TestController {@GetMapping("/download")public ResponseEntity<Resource> downloadFile() throws IOException {// 读取文件内容byte[] bytes = Files.readAllBytes(Paths.get("/Users/hsk/DevCodes/Gitlab/JavaNetworkL/SpringBootDemo/file/wallhaven-l3kq9y.png"));ByteArrayResource resource = new ByteArrayResource(bytes);// 设置响应头部信息HttpHeaders headers = new HttpHeaders();headers.setContentDisposition(ContentDisposition.builder("attachment").filename("a.png").build());headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);// 返回ResponseEntity,包含文件内容和响应头return ResponseEntity.ok().headers(headers).body(resource);}}

附录 8 | 接收 Post 请求且接受 HttpHeaders

@PostMapping("/post2")
public String post2(@RequestHeader HttpHeaders headers,@RequestBody User user) {System.out.println("接收成功:" + user);return "接收成功,头部信息为:" + headers + "User对象为:"+user;
}

附录 9 | 接收 Put 请求

@PutMapping("/put2")
public String put2(@RequestBody User user) {return "更新用户信息为"+user;
}

结语

当然,除了以上介绍的三种调用方式之外,Java以及相关框架还有许多十分优秀的网络通讯工具,篇幅有限这里不多做介绍,感兴趣的小伙伴可以自己研究一下!!求关注咯!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/30367.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IPFoxy代理IP:IPv4与IPv6性能与安全性对比

在使用IPFoxy静态代理IP的过程中&#xff0c;经常有小白朋友疑惑&#xff0c;IPv4与IPv6有何区别&#xff1f;他们在性能与安全上的差别如何&#xff0c;又该如何选择&#xff1f;在这篇博文中&#xff0c;我们将从各个方面为您科普这一区别&#xff0c;帮助您更好的选择。 一、…

柒拾肆- 如何通过数据影响决策(五)- 时序数据

什么是好&#xff1f; 对于 单一时序指标 来说&#xff08;如最简单的 销售、日活顾客数、订单数 等 &#xff09;&#xff0c;在决策时该怎样 看 呢&#xff1f; 或者换句话说&#xff0c;数据 应该怎样 表达 才能让决策者做出 更全面 的决策呢&#xff1f; 一、目前的方式 …

Mkdocs中文系列教程补充(1)

什么是requirements.txt 我的理解是mkdocs依赖的py库 第一次建立MKdocs文档使用 mkdocs new . 完后&#xff0c;比较建议执行一下&#xff1a; pip install -r requirements.txt 不然mkdocs serve后会出现什么 xxx not found &#xff0c;比如下面这位老哥 示例 mkdocs …

【C++】循环、控制流语句、

8、循环&#xff08;loops&#xff09;&#xff08;1&#xff09;for loops for循环非常灵活&#xff0c;可以做很多事情。上图红框框出来的代码块就是一个for循环。 for是关键字 for后面内容分为三部分&#xff0c;每部分用分号&#xff1b;隔开 第一部分A是变量的声明&…

Qt入门小项目 | 实现一个图片查看器

文章目录 一、实现一个图片查看软件 一、实现一个图片查看软件 需要实现的功能&#xff1a; 打开目录选择图片显示图片的名字显示图片 在以上功能的基础上进行优化&#xff0c;需要解决如下问题&#xff1a; 如何记住上次打开的路径&#xff1f; 将路径保存到配置文件中&#x…

使用MAT定位线上OOM问题

目录 1.什么是OOM? 2.发生的可能原因 3.常见类型的OOM 4.如何定位问题&#xff1f; 4.1 获取dump文件 4.2 MAT分析 「Leak Suspects」泄露嫌疑 「Histogram」直方图 「dominator tree」支配树 「thread overview」线程视图 目录 1.什么是OOM? 2.发生的可能原因 …

深度学习入门5——为什么神经网络可以学习?

在理解神经网络的可学习性之前&#xff0c;需要先从数学中的导数、数值微分、偏导数、梯度等概念入手&#xff0c;从而理解为什么神经网络具备学习能力。 1.数值微分的定义 先从导数出发理解什么是梯度。某一点的导数直观理解就是在该点的切线的斜率。在数学中导数表示某个瞬…

05-对混合app应用中的元素进行定位

本文介绍对于混合app应用中的元素如何进行定位。 一、app的类型 1&#xff09;Native App&#xff08;原生应用&#xff09; 原生应用是指利用Android、IOS平台官方的开发语言、开发类库、工具等进行开发的app应用&#xff0c;在应用性能和交互体验上应该是最好的。 通俗点来…

Win10录屏,3种方法,快速搞定

在数字化时代&#xff0c;录屏功能已经成为了我们日常生活和工作中不可或缺的一部分。无论是为了制作教学视频&#xff0c;还是为了记录游戏的高光时刻&#xff0c;还是为了保存开会内容&#xff0c;录屏功能都能为我们提供极大的便利。Win10操作系统作为微软推出的经典之作&am…

在k8s中部署Elasticsearch高可用集群详细教程

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《洞察之眼&#xff1a;ELK监控与可视化》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、Elasticsearch简介 2、为什么在k8s中部署elasti…

Redis 6.0新特性详解

Redis 6.0新特性主要有3个&#xff1a;多线程、Client Side Cache、Acls。下面详细说明一下。 1.多线程 redis 6.0 提供了多线程的支持&#xff0c;redis 6 以前的版本&#xff0c;严格来说也是多线程&#xff0c;只不过执行用户命令的请求时单线程模型&#xff0c;还有一些线…

鸿蒙HarmonyOS实战:状态管理和传值

状态管理 State State是一个装饰器&#xff0c;是用来存放数据的&#xff0c;比较好理解 由State的数据来进行状态驱动视图更新 代码很简单 State count: number 0; 需要注意的是State初始化的数据必须赋值 这里我们讨论简单用法&#xff0c;至于复杂的用法可以到项目中介绍…

RAG 流程及论文串烧

文档切片 文档切片的五个层次 https://medium.com/anuragmishra_27746/five-levels-of-chunking-strategies-in-rag-notes-from-gregs-video-7b735895694d#b123 Basic RAG 与 Advanced RAG https://pub.towardsai.net/advanced-rag-techniques-an-illustrated-overview-04d…

Python学习笔记14:进阶篇(三)。类的终结篇,类的导入和模块的导入。

前言 这篇文章属于类知识的最后一篇&#xff0c;带一点点其他知识&#xff0c;学习内容来自于Python crash course。 关注我私信发送Python crash course&#xff0c;分享一份中文版PDF。 类的导入 在学习的时候&#xff0c;包括之前&#xff0c;我都是在一个文件中把所有代…

免费域名第二弹:手把手教你获取个性化免费域名并托管至Cloudflare

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 免费申请域名的方法 📒📝 注册账号📝 创建免费域名📝 将域名添加到 Cloudflare⚓️ 相关链接 ⚓️📖 介绍 📖 在如今的数字时代,拥有一个个性化的域名已经成为越来越多人的需求。无论是建立个人博客、项目展示,还…

鸿蒙开发通信与连接:【@ohos.connectedTag (有源标签)】

有源标签 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import connectedTag from ohos.connectedTag;connectedTag.init init(): boolean 初始化有源标签芯片。 需要权限&#…

MySQL经典面试题:谈一谈你对事务的理解

文章目录 &#x1f4d1;事务事务的基本概念回滚开启事务的sql语句 事务的基本特性总结一下涉及到的三个问题 ☁️结语 &#x1f4d1;事务 事务的基本概念 事务是用来解决一类特定场景的问题的&#xff0c;在有些场景中&#xff0c;完成某个操作&#xff0c;需要多个sql配合完…

CPN IDE实现分层效果

Shift键鼠标选中要分层的库所和变迁&#xff01;然后create subpage。 Subpage是这样的&#xff0c;不会像CPN tools里面自动生成IN和OUT库所&#xff0c;但是也能正确运行。 虽然父页面在运行中有标红&#xff1a;"port not defined" 错误通常意味着在模型中有一些连…

【QT5】<重点> QT多线程

文章目录 前言 一、QThread创建多线程 二、QMutex基于互斥量的同步 三、QReadWriteLock线程同步 四、QWaitCondition线程同步 五、QSemaphore基于信号量的同步 前言 本篇记录学习QT多线程的知识&#xff0c;参考视频13.1QThread创建多线程程序_哔哩哔哩。若涉及版权问题…