package com.example.test1.service; import com.example.test1.component.WordToHtmlConverter; import com.jcraft.jsch.; import org.apache.poi.xwpf.usermodel.; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.; import java.nio.file.; import java.util.UUID; @Service public class FileProcessingService { // 远程服务器配置 private static final String REMOTE_HOST = “”; private static final int REMOTE_PORT = ; private static final String REMOTE_USER = “”; private static final String REMOTE_PASSWORD = “#”; private static final String REMOTE_BASE_DIR = “////”; private static final String BASE_URL = “/images/”; private static final String LOCAL_TEMP_DIR = System.getProperty(“java.io.tmpdir”) + “/service_files/”; public String processInput(Object input) throws Exception { if (input instanceof String) { return processText((String) input); } else if (input instanceof MultipartFile) { return processFile((MultipartFile) input); } throw new IllegalArgumentException(“Unsupported input type”); } private String processText(String text) { // 转义HTML特殊字符 String escapedText = escapeHtml(text); // 保留换行符 - 将换行符转换为 标签 String withBreaks = escapedText.replace(“\n”, “ ”); // 直接返回HTML内容 return “ ” + withBreaks + “ ”; } private String processFile(MultipartFile file) throws Exception { String originalName = file.getOriginalFilename(); if (originalName == null) throw new IllegalArgumentException(“Invalid file name”); String extension = originalName.substring(originalName.lastIndexOf(“.”) + 1).toLowerCase(); switch (extension) { case “jpg”: case “jpeg”: case “png”: case “gif”: // 生成包含图片引用的HTML String imageUrl = uploadImage(file.getBytes(), extension); return “<img src="” + imageUrl + “" alt="Service Image">”; case “doc”: case “docx”: // 转换Word文档为HTML return convertToHtml(file.getInputStream()); default: throw new IllegalArgumentException(“Unsupported file type: " + extension); } } /** * 上传图片到远程服务器 * @param imageData 图片字节数组 * @param extension 文件扩展名 * @return 图片URL */ public String uploadImage(byte[] imageData, String extension) throws Exception { String fileName = “img_” + UUID.randomUUID() + “.” + extension; Path tempPath = saveTempFile(imageData, fileName); uploadViaSftp(tempPath, fileName); return BASE_URL + fileName; } private void uploadViaSftp(Path localPath, String remoteFileName) throws Exception { JSch jsch = new JSch(); Session session = jsch.getSession(REMOTE_USER, REMOTE_HOST, REMOTE_PORT); session.setPassword(REMOTE_PASSWORD); session.setConfig(“StrictHostKeyChecking”, “no”); session.connect(); ChannelSftp channel = (ChannelSftp) session.openChannel(“sftp”); channel.connect(); // 确保远程目录存在 try { channel.cd(REMOTE_BASE_DIR); } catch (SftpException e) { channel.mkdir(REMOTE_BASE_DIR); channel.cd(REMOTE_BASE_DIR); } // 上传文件 channel.put(localPath.toString(), remoteFileName); // 清理本地文件 Files.deleteIfExists(localPath); channel.disconnect(); session.disconnect(); } private Path saveTempFile(byte[] data, String fileName) throws IOException { Path dirPath = Paths.get(LOCAL_TEMP_DIR); if (!Files.exists(dirPath)) { Files.createDirectories(dirPath); } Path filePath = dirPath.resolve(fileName); Files.write(filePath, data); return filePath; } public String convertToHtml(InputStream docxStream) throws Exception { try (XWPFDocument document = new XWPFDocument(docxStream)) { StringBuilder htmlBuilder = new StringBuilder(); htmlBuilder.append(”“); // 处理段落 for (XWPFParagraph paragraph : document.getParagraphs()) { htmlBuilder.append(processParagraph(paragraph)); } // 处理表格 for (XWPFTable table : document.getTables()) { htmlBuilder.append(processTable(table)); } htmlBuilder.append(”“); return htmlBuilder.toString(); } } private String processParagraph(XWPFParagraph paragraph) throws Exception { StringBuilder paraBuilder = new StringBuilder(); boolean isFirstRun = true; for (XWPFRun run : paragraph.getRuns()) { String text = run.getText(0); // 处理图片 if (run.getEmbeddedPictures().size() > 0) { for (XWPFPicture picture : run.getEmbeddedPictures()) { XWPFPictureData picData = picture.getPictureData(); // 上传图片并获取URL String imageUrl = uploadImage( picData.getData(), picData.suggestFileExtension() ); paraBuilder.append(”<img src="“).append(imageUrl).append(”" alt="">“); } } if (text != null && !text.trim().isEmpty()) { // 处理换行符 String processedText = text.replace(”\n", “ ”); // 处理文本样式 if (run.isBold()) paraBuilder.append(“”); if (run.isItalic()) paraBuilder.append(“”); paraBuilder.append(escapeHtml(processedText)); if (run.isItalic()) paraBuilder.append(“”); if (run.isBold()) paraBuilder.append(“”); } // 检测换行符(Word中的硬回车) if (run.getCTR().getBrArray().length > 0) { paraBuilder.append(“ ”); } isFirstRun = false; } // 如果是空段落,保留换行效果 if (paraBuilder.length() == 0) { return “ ”; } return “ ” + paraBuilder.toString() + “ ”; } private String processTable(XWPFTable table) throws Exception { StringBuilder tableBuilder = new StringBuilder(“<table border="1">”); for (XWPFTableRow row : table.getRows()) { tableBuilder.append(“”); for (XWPFTableCell cell : row.getTableCells()) { tableBuilder.append(“”); for (XWPFParagraph para : cell.getParagraphs()) { tableBuilder.append(processParagraph(para)); } tableBuilder.append(“”); } tableBuilder.append(“”); } tableBuilder.append(“”); return tableBuilder.toString(); } private String escapeHtml(String text) { return text.replace(“&”, “&”) .replace(“<”, “<”) .replace(“>”, “>”) .replace(“"”, “"”) .replace(“'”, “'”); } }修改之前的解决方案,支持文体和图片同时传入,且图片支持多图片传入,转为HTML。保留word上传转为HTML功能。图片都存入服务器
时间: 2025-07-23 16:01:42 浏览: 5
<think>我们有一个需求:支持文本和图片同时传入,且图片支持多图片传入,转为HTML。同时保留word上传转为HTML功能。图片都存入服务器。
分析当前代码:
1. 当前`processInput`方法只支持单一输入(要么是文本,要么是文件)。现在需要同时传入文本和多个图片。
2. 因此,我们需要修改输入方式,不能只使用一个`Object`,而是需要设计一个新的数据结构来同时接收文本和多个图片。
解决方案:
1. 创建一个新的DTO(数据传输对象)来封装请求数据,包含一个文本字段和一个图片列表(MultipartFile数组)。
2. 修改服务方法,接收这个DTO对象,然后处理文本和图片,生成一个完整的HTML字符串。
但是,注意原代码中还有处理Word文件的功能。我们需要保留这个功能,同时新增同时传入文本和图片的功能。
我们可以这样设计:
方案A:扩展`processInput`方法,使其能够接收一个复合对象(包含文本和文件列表),同时为了兼容原来的单一文本或单一文件,我们可以保留原有方法,但这样可能会造成混淆。
方案B:重新设计一个服务方法,专门用于处理复合输入(文本+多图)。同时保留原有的处理单一文本或单一文件的方法。
考虑到原有代码中`processInput`方法被设计为处理单一输入,现在需求变化,我们可能需要一个新的入口。
因此,我们可以在服务类中新增一个方法,比如:
```java
public String processRichContent(String text, List<MultipartFile> images) throws Exception
```
这个方法将文本和图片列表作为参数,然后:
- 首先处理文本,将文本中的换行符转换为`<br>`,并转义HTML特殊字符。
- 然后处理每一张图片,将图片上传到服务器,并生成`<img>`标签。
- 最后将文本部分和图片标签组合成一个完整的HTML片段。
但是,注意:图片在HTML中的位置?用户可能希望图片插入在文本的特定位置。然而,我们的输入是分离的:一段文本和一组图片。我们无法知道图片应该插入到文本的哪个位置。
因此,我们需要改变策略:允许用户在文本中使用特定的占位符来表示图片的位置。例如,用户可以在文本中写`[image1]`,`[image2]`等,然后我们将这些占位符替换为对应的图片标签。
但是,这个需求需要明确:用户如何指定图片位置?如果需求不明确,我们可以先按照顺序将图片依次附加在文本后面。
假设需求是:图片按照传入顺序,依次放在文本的后面。
那么,我们可以这样实现:
- 将文本转换为HTML(保留换行,转义特殊字符)。
- 遍历图片列表,对每张图片上传,生成一个`<img>`标签,并追加到文本HTML后面。
但是,如果需求是图片可以插入在文本中间,那么就需要用户提供标记。这里我们先按照简单的方式:图片放在文本后面。
步骤:
1. 处理文本:将文本中的换行符替换为`<br>`,并转义HTML。
2. 对于每张图片,调用`uploadImage`方法上传,得到图片URL,生成`<img>`标签,并追加到一个字符串中。
3. 将图片标签字符串追加到处理后的文本后面。
另外,我们还需要保留Word文件处理的功能。这个功能在原有方法中已经存在,我们不需要改变它,因为它是通过上传Word文件来处理的。
修改代码:
1. 首先,我们修改`FileProcessingService`,增加一个新的方法`processRichContent`,用于处理文本和图片列表。
2. 同时,我们注意到原代码中`uploadImage`方法已经是公有的,我们可以直接使用它。
3. 由于我们新增了方法,需要确保远程服务器配置等是正确可用的。
4. 另外,原代码中处理文本的方法`processText`是私有的,我们可以将其重构为一个公共的静态方法(或者至少是同一个类内部可复用的方法),因为现在我们需要在两个地方使用(处理纯文本和富文本中的文本部分)。
5. 但是,注意:在`processRichContent`中,我们不需要将文本包裹在`<p>`标签中,因为图片也要追加在后面。我们可以将文本处理为一个字符串,然后加上图片标签。
6. 因此,我们可以修改`processText`方法,使其不再包裹`<p>`标签,或者新建一个方法只做转义和换行处理,然后由调用方决定如何包裹标签。
但是,为了最小化修改,我们可以在`processRichContent`中:
先调用`processText`方法,但是`processText`方法返回的是包裹了`<p>`标签的字符串。这样,图片就会在`<p>`标签之外。
这样也可以,因为图片标签是块级元素(或者我们可以用div包裹),所以放在段落后面是合理的。
所以,我们可以这样写:
```java
public String processRichContent(String text, List<MultipartFile> images) throws Exception {
// 处理文本部分
String textHtml = processText(text); // 注意:processText返回的是<p>...</p>,并且已经转义和换行处理
// 处理图片部分
StringBuilder imagesHtml = new StringBuilder();
for (MultipartFile image : images) {
if (image.isEmpty()) {
continue;
}
String originalName = image.getOriginalFilename();
if (originalName == null) {
continue;
}
String extension = originalName.substring(originalName.lastIndexOf('.') + 1).toLowerCase();
if (Arrays.asList("jpg", "jpeg", "png", "gif").contains(extension)) {
String imageUrl = uploadImage(image.getBytes(), extension);
imagesHtml.append("<img src=\"").append(imageUrl).append("\" alt=\"Uploaded image\" style=\"max-width:100%;\">");
}
}
return textHtml + imagesHtml.toString();
}
```
但是,这样处理的话,文本部分已经是一个完整的段落,而图片部分是一系列图片标签。这样返回的HTML片段可以放在一个div中吗?其实直接返回也是可以的,因为HTML中可以有多个根元素(虽然严格来说应该只有一个根,但浏览器通常可以处理多个并列元素)。
但是,为了更好的结构,我们可以将整个内容包裹在一个div中。
修改为:
```java
return "<div>" + textHtml + imagesHtml.toString() + "</div>";
```
但是,原`processText`返回的已经是`<p>...</p>`,所以外面再包裹一层div也可以。
但是,注意:原`processText`方法中,如果传入的是多行文本,它已经将换行符转换成了`<br>`,并且整个文本放在一个`<p>`标签中。这可能会造成一个问题:如果文本很长,整个文本都在一个段落中。而实际上,原方法就是如此设计的。
因此,我们不做修改。
另外,我们还需要考虑:如果文本为空,那么只返回图片;如果图片列表为空,只返回文本。
现在,我们需要修改Controller层,因为原来的Controller可能是这样:
```java
@PostMapping("/process")
public String processInput(@RequestParam("input") Object input) {
// 调用fileProcessingService.processInput(input)
}
```
现在,我们需要新增一个端点来支持同时上传文本和多个图片。
但是,由于题目要求修改服务,我们只关注服务层。Controller的修改不在本题范围内,但我们要确保服务层提供这个新方法。
另外,原代码中处理Word文件的功能不变,所以我们保留`processFile`方法。
重构`processText`方法:由于在`processRichContent`中我们调用了`processText`,而`processText`返回的是带`<p>`标签的字符串。但是,在富文本处理中,我们希望文本部分是一个段落(或者多个段落?)。目前,`processText`是将整个文本作为一个段落。如果用户输入中有多个连续换行,那么会被转换为多个`<br>`,但还是在同一个段落中。这符合一般需求。
因此,我们不做修改。
但是,注意:在`processRichContent`中,我们上传图片使用的是`uploadImage`方法,这个方法会通过SFTP上传到远程服务器。同时,我们处理了图片的扩展名,只支持jpg, jpeg, png, gif。
最后,我们还需要处理异常,比如图片上传失败等。这里我们简单抛出异常,实际应用中可能需要更细致的错误处理。
修改后的`FileProcessingService`代码:
注意:由于代码较长,我们只展示修改和新增的部分。
1. 新增`processRichContent`方法。
2. 由于`processText`是私有的,我们可以在类内部调用。但是,如果希望外部也能处理纯文本,可以保持原样。
但是,在`processRichContent`中,我们调用了`processText`,它返回的是带`<p>`标签的字符串。如果我们希望文本部分不包含`<p>`标签,那么就需要重构。但这里按照原设计。
另外,注意:`processText`方法中,如果传入的是空字符串,它也会返回`<p></p>`。这样也可以。
代码修改如下:</think>为了支持文本和图片同时传入,并实现多图片上传功能,同时保留Word转HTML功能,我对代码进行了以下关键修改:
### 修改后的代码实现
```java
package com.example.test1.service;
import com.example.test1.component.WordToHtmlConverter;
import com.jcraft.jsch.*;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.nio.file.*;
import java.util.*;
@Service
public class FileProcessingService {
// 远程服务器配置
private static final String REMOTE_HOST = "";
private static final int REMOTE_PORT = ;
private static final String REMOTE_USER = "";
private static final String REMOTE_PASSWORD = "#";
private static final String REMOTE_BASE_DIR = "////";
private static final String BASE_URL = "/images/";
private static final String LOCAL_TEMP_DIR = System.getProperty("java.io.tmpdir") + "/service_files/";
// 处理富文本输入(文本+多张图片)
public String processRichContent(String text, List<MultipartFile> imageFiles) throws Exception {
StringBuilder htmlBuilder = new StringBuilder();
// 1. 处理文本部分
if (text != null && !text.trim().isEmpty()) {
htmlBuilder.append(processText(text));
}
// 2. 处理多张图片
if (imageFiles != null && !imageFiles.isEmpty()) {
for (MultipartFile imageFile : imageFiles) {
if (!imageFile.isEmpty()) {
String imageHtml = processImageFile(imageFile);
htmlBuilder.append(imageHtml);
}
}
}
return "<div class=\"rich-content\">" + htmlBuilder.toString() + "</div>";
}
// 处理单个图片文件
private String processImageFile(MultipartFile imageFile) throws Exception {
String originalName = imageFile.getOriginalFilename();
if (originalName == null) throw new IllegalArgumentException("Invalid file name");
String extension = originalName.substring(originalName.lastIndexOf(".") + 1).toLowerCase();
if (Arrays.asList("jpg", "jpeg", "png", "gif").contains(extension)) {
String imageUrl = uploadImage(imageFile.getBytes(), extension);
return "<img src=\"" + imageUrl + "\" class=\"uploaded-image\" alt=\"Uploaded image\">";
}
throw new IllegalArgumentException("Unsupported image type: " + extension);
}
// 保留原有Word处理功能
public String processWordDocument(MultipartFile wordFile) throws Exception {
return convertToHtml(wordFile.getInputStream());
}
// 文本处理(保持不变)
private String processText(String text) {
String escapedText = escapeHtml(text);
String withBreaks = escapedText.replace("\n", "<br>");
return "<p>" + withBreaks + "</p>";
}
/** 图片上传方法(保持不变)*/
public String uploadImage(byte[] imageData, String extension) throws Exception {
String fileName = "img_" + UUID.randomUUID() + "." + extension;
Path tempPath = saveTempFile(imageData, fileName);
uploadViaSftp(tempPath, fileName);
return BASE_URL + fileName;
}
// SFTP上传和Word处理等辅助方法保持不变...
// (包括uploadViaSftp, saveTempFile, convertToHtml, processParagraph等)
// HTML转义方法保持不变
private String escapeHtml(String text) {
return text.replace("&", "&")
.replace("<", "<")
.replace(">", ">")
.replace("\"", """)
.replace("'", "'");
}
}
```
### 关键修改说明
1. **新增富文本处理方法**:
```java
public String processRichContent(String text, List<MultipartFile> imageFiles)
```
- 同时接收文本内容和图片列表
- 分别处理文本和图片后组合成完整HTML
2. **多图片处理逻辑**:
```java
for (MultipartFile imageFile : imageFiles) {
if (!imageFile.isEmpty()) {
String imageHtml = processImageFile(imageFile);
htmlBuilder.append(imageHtml);
}
}
```
- 循环处理每张图片
- 支持JPG/PNG/GIF等常见格式
- 每张图片生成独立的<img>标签
3. **图片处理方法**:
```java
private String processImageFile(MultipartFile imageFile)
```
- 专门处理单张图片的上传和标签生成
- 复用原有的`uploadImage`上传逻辑
4. **保留Word处理功能**:
```java
public String processWordDocument(MultipartFile wordFile)
```
- 独立方法处理Word文档转换
- 与富文本处理完全分离
5. **HTML结构优化**:
```java
return "<div class=\"rich-content\">" + ... + "</div>";
```
- 添加容器div便于前端样式控制
- 添加CSS类名`rich-content`和`uploaded-image`
### 使用示例
```java
// 在Controller中使用
@PostMapping("/upload/rich-content")
public String uploadRichContent(
@RequestParam("content") String text,
@RequestParam("images") List<MultipartFile> imageFiles) {
return fileProcessingService.processRichContent(text, imageFiles);
}
// 单独处理Word文档
@PostMapping("/upload/word")
public String uploadWordDocument(@RequestParam("file") MultipartFile wordFile) {
return fileProcessingService.processWordDocument(wordFile);
}
```
### 解决方案特点
1. **文本与图片分离处理**:
- 文本部分:保留换行符转换和HTML转义
- 图片部分:支持批量上传和多图展示
2. **图片处理优化**:
- 每张图片生成唯一文件名(UUID)
- 添加CSS类名便于样式控制
- 保持原有SFTP上传逻辑
3. **兼容性设计**:
- 完全保留Word文档转换功能
- 空文本或多图片自动跳过处理
- 严格的文件类型校验
4. **HTML结构规范**:
- 文本包裹在<p>标签中
- 图片使用独立<img>标签
- 整体内容包裹在容器div中
阅读全文
相关推荐














