reactive-01

news/2025/10/31 2:17:46/文章来源:https://www.cnblogs.com/lihuadaiyu/p/19178465

package com.hyman.service;

import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.MultipartBodyBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

@Service
public class ReactiveFileUploadService {

private final WebClient webClient;

public ReactiveFileUploadService(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder.build();
}

/**
* 使用 Reactive WebClient 上传文件到第三方接口
*/
public Mono<String> uploadFile(String filePath, String uploadUrl) {
return Mono.fromCallable(() -> {
// 读取文件
Path path = Paths.get(filePath);
byte[] fileBytes = Files.readAllBytes(path);
String fileName = path.getFileName().toString();

return new FileResource(fileBytes, fileName);
})
.flatMap(fileResource -> {
// 构建 multipart 请求
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder();
bodyBuilder.part("file", fileResource)
.filename(fileResource.getFilename())
.contentType(MediaType.APPLICATION_OCTET_STREAM);

return webClient.post()
.uri(uploadUrl)
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(BodyInserters.fromMultipartData(bodyBuilder.build()))
.retrieve()
.bodyToMono(String.class);
})
.onErrorResume(e -> Mono.just("上传失败: " + e.getMessage()));
}

/**
* 自定义文件资源类
*/
private static class FileResource extends ByteArrayResource {
private final String filename;

public FileResource(byte[] byteArray, String filename) {
super(byteArray);
this.filename = filename;
}

@Override
public String getFilename() {
return filename;
}
}
}



========================


package com.hyman.service;

import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.MultipartBodyBuilder;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

@Service
public class AdvancedReactiveFileUploadService {

private final WebClient webClient;

public AdvancedReactiveFileUploadService(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder.baseUrl("").build();
}

/**
* 流式上传大文件 - 避免内存溢出
*/
public Mono<UploadResult> uploadLargeFile(String filePath, String uploadUrl,
Map<String, String> headers,
Map<String, String> formParams) {
return Mono.fromCallable(() -> Paths.get(filePath))
.flatMap(path -> {
if (!Files.exists(path) || !Files.isRegularFile(path)) {
return Mono.just(UploadResult.error("文件不存在: " + filePath));
}

try {
long fileSize = Files.size(path);
String fileName = path.getFileName().toString();

// 构建 multipart 请求
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder();

// 添加表单参数
if (formParams != null) {
formParams.forEach((key, value) ->
bodyBuilder.part(key, value));
}

// 添加文件(使用 FileSystemResource 避免加载到内存)
bodyBuilder.part("file", new FileSystemResource(path))
.filename(fileName)
.contentType(MediaType.APPLICATION_OCTET_STREAM);

// 构建请求头
WebClient.RequestHeadersSpec<?> requestSpec = webClient.post()
.uri(uploadUrl)
.contentType(MediaType.MULTIPART_FORM_DATA);

// 添加自定义请求头
if (headers != null) {
headers.forEach(requestSpec::header);
}

return requestSpec
.body(BodyInserters.fromMultipartData(bodyBuilder.build()))
.retrieve()
.toEntity(String.class)
.map(response -> UploadResult.success(
response.getStatusCodeValue(),
response.getBody(),
fileName,
fileSize
))
.onErrorResume(e -> Mono.just(UploadResult.error(
"上传失败: " + e.getMessage())));

} catch (IOException e) {
return Mono.just(UploadResult.error("文件读取失败: " + e.getMessage()));
}
});
}

/**
* 上传多个文件
*/
public Mono<UploadResult> uploadMultipleFiles(String[] filePaths, String uploadUrl) {
return Flux.fromArray(filePaths)
.flatMap(filePath -> uploadLargeFile(filePath, uploadUrl, null, null))
.collectList()
.map(results -> {
long successCount = results.stream().filter(UploadResult::isSuccess).count();
return UploadResult.batchResult(results, successCount, results.size());
});
}

/**
* 使用 Flux<DataBuffer> 进行真正的流式上传
*/
public Mono<UploadResult> streamUpload(String filePath, String uploadUrl) {
return Mono.fromCallable(() -> Paths.get(filePath))
.flatMap(path -> {
try {
Flux<DataBuffer> dataBufferFlux = DataBufferUtils.read(
path,
DefaultDataBufferFactory.sharedInstance,
4096
);

MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder();
bodyBuilder.asyncPart("file", dataBufferFlux, DataBuffer.class)
.filename(path.getFileName().toString());

return webClient.post()
.uri(uploadUrl)
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(BodyInserters.fromMultipartData(bodyBuilder.build()))
.retrieve()
.bodyToMono(String.class)
.map(response -> UploadResult.success(200, response,
path.getFileName().toString(), Files.size(path)))
.onErrorResume(e -> Mono.just(UploadResult.error(e.getMessage())));

} catch (IOException e) {
return Mono.just(UploadResult.error("文件读取失败: " + e.getMessage()));
}
});
}

/**
* 上传结果类
*/
public static class UploadResult {
private boolean success;
private int statusCode;
private String message;
private String response;
private String fileName;
private long fileSize;
private String error;

// 静态工厂方法
public static UploadResult success(int statusCode, String response, String fileName, long fileSize) {
UploadResult result = new UploadResult();
result.success = true;
result.statusCode = statusCode;
result.response = response;
result.fileName = fileName;
result.fileSize = fileSize;
result.message = "上传成功";
return result;
}

public static UploadResult error(String error) {
UploadResult result = new UploadResult();
result.success = false;
result.error = error;
result.message = "上传失败";
return result;
}

public static UploadResult batchResult(java.util.List<UploadResult> results,
long successCount, long totalCount) {
UploadResult result = new UploadResult();
result.success = successCount == totalCount;
result.message = String.format("批量上传完成: 成功 %d/%d", successCount, totalCount);
return result;
}

// getter 方法
public boolean isSuccess() { return success; }
public int getStatusCode() { return statusCode; }
public String getMessage() { return message; }
public String getResponse() { return response; }
public String getFileName() { return fileName; }
public long getFileSize() { return fileSize; }
public String getError() { return error; }

@Override
public String toString() {
return String.format(
"UploadResult{success=%s, statusCode=%d, fileName='%s', fileSize=%d, message='%s'}",
success, statusCode, fileName, fileSize, message
);
}
}
}

 

 

 

===========

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

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

相关文章

revit api获取excursion 拉伸体的轮廓和平面sketchplane GeometryCreationUtilities

revit api获取excursion 拉伸体的轮廓和平面sketchplane GeometryCreationUtilitiespublic void GetSketchFromExtrusion() {Document doc = this.ActiveUIDocument.Document;Extrusion extrusion = doc.GetEleme…

revit api创建模型线

revit api创建模型线public void CreateModelCurve() {Document doc = this.ActiveUIDocument.Document; // 在族文档中找到名字为"Ref. Level"的标高FilteredElementCollector collector = new Filtered…

revit 创建sketchplane

revit 创建sketchplanesketchplane在创建某些图元时候可以用到 public void CreateSketchPlaneByPlane() {Document doc = this.ActiveUIDocument.Document; using(Transaction trans = new Transaction(doc, "…

revit api创建参考线

revit api创建参考线参考线可以用modelcurve转化而来 public void ChangeModelCurveToReferenceLine() { Document doc = this.ActiveUIDocument.Document; ModelCurve modelCurve = doc.GetElement(new…

【AI说HTML 04】掌握核心文档结构标签的方法

要真正掌握HTML的核心文档结构标签,关键在于理解它们如何像搭积木一样,共同构建起一个清晰、语义明确的网页骨架。下面这个流程图概括了一个网页的典型结构,帮你先建立整体印象: flowchart TDA[<!DOCTYPE html&…

revit api创建参考平面

revit api创建参考平面public void CreatReferencePlane() {Document doc = this.ActiveUIDocument.Document; if(!doc.IsFamilyDocument)return;using(Transaction transaction = new Transaction(doc, "Edit…

【AI说HTML 02】如何深刻建立对HTML的正确认知

要深刻建立对HTML的正确认知,关键在于理解它不仅仅是用来“画”出网页样子的工具,更是一套用于描述内容结构和含义的语言。下面这个表格梳理了理解HTML时需要建立的核心认知维度。认知维度 错误认知(新手常见) 正确…

【AI说HTML 03】手把手教你搭建极简HTML开发环境

为你打造一个极简且高效的HTML开发环境,其实只需要几个核心步骤。下面这张流程图概括了全过程,你可以先有一个整体的了解,再跟着后续的详细步骤操作。 flowchart TDA[下载并安装VS Code] --> B[安装必备插件<…

图元编辑 移动 旋转 镜像等

图元编辑 移动 旋转 镜像等编辑1 Document projectDoc = ActiveUIDocument.Document;using(Transaction moveColumnTran = new Transaction(projectDoc, "Move a new column to the new place")) {moveCol…

【AI说】如何深刻建立对HTML的正确认知

要深刻建立对HTML的正确认知,关键在于理解它不仅仅是用来“画”出网页样子的工具,更是一套用于描述内容结构和含义的语言。下面这个表格梳理了理解HTML时需要建立的核心认知维度。认知维度 错误认知(新手常见) 正确…

计算机组成原理核心知识点梳理

SQLAlchemy是Python中最流行的ORM(对象关系映射)框架之一,它提供了高效且灵活的数据库操作方式。本文将介绍如何使用SQLAlchemy ORM进行数据库操作。 目录 安装SQLAlchemy 核心概念 连接数据库 定义数据模型 创建数…

revit api 内置族类型 walltype

revit api 内置族类型 walltype复制墙类型 Wall wall = RevitDoc.GetElement(new ElementId(185521)) as Wall; WallType wallType = wall.WallType; ElementType duplicatedWallType = wallType.Duplicate(wallType.…

国产的编程语言

​1、非修改序列算法 这些算法不会改变它们所操作的容器中的元素。 1.1 find 和 find_if find(begin, end, value):查找第一个等于 value 的元素,返回迭代器(未找到返回 end)。 find_if(begin, end, predicate):查…

revit api族文件图元编辑 familyitem factory

revit api族文件图元编辑 familyitem factory创建拉伸实体 //创建族文档 Document familyDoc = RevitApp.NewFamilyDocument(@"C:\ProgramData\Autodesk\RVT 2014\Family Templates\Chinese\公制常规模型.rft&qu…

【AI说】HTML从零基础到精通路径

学习HTML从零基础到精通,是一个循序渐进的过程,关键在于建立正确的认知、通过实践巩固知识,并逐步关注更宏观的网页质量维度。下面我为你梳理一条清晰的路径,并附上每个阶段的学习重点和建议。 为了让你对整体学习…

revit api 加载族文件+放置族实例 创建门图元

revit api 加载族文件+放置族实例 创建门图元string doorTypeName = "0762 x 2032 mm"; FamilySymbol doorType = null;// 在文档中找到名字为"0762 x 2032 mm"的门类型 ElementFilter doorCateg…

一件有关山寨iPod shuffle 2的往事

说起来,我其实在这两个月才知道,我当年买的是iPod shuffle 2的一个山寨货。 其实那会我们连苹果都不太清楚是啥,只知道是一家手机厂商。 记得差不多是2015-2017年那会,应该是2016年到2017年这段时间,当时我的手机…

revit api共享参数

revit api共享参数获取共享参数 // 打开共享参数文件 DefinitionFile definitionFile = RevitApp.OpenSharedParameterFile(); // 获取参数组的集合 DefinitionGroups groups = definitionFile.Groups;foreach (Defini…

[Vulhub]Sickos靶机渗透

[Vulhub靶机]Sick0s靶机渗透 靶机搭建 下载地址: https://download.vulnhub.com/sickos/sick0s1.1.7z 下载后设置nat模式即可与攻击机同一网段 kali:192.168.88.133 1.信息收集 主机发现 sudo nmap -sn 192.168.88.0/2…

《代码大全2》读书笔记2

《代码大全2》第四章聚焦“关键的构建决策”,核心是让开发者明白,构建阶段的前期选择比后期修改更重要,这些决策直接影响代码的后续维护成本。首先是语言选择,书中明确“无最优语言,只有最适配场景”,比如对性能…