Совет: После написания статьи оглавление может быть сгенерировано автоматически. О том, как его создать, см. в справочном документе справа.
vue+ueditor+springboot, реализовать загрузку и редактирование документов Word
`Внешний интерфейс импортирует документы Word (поддерживаются форматы doc и docx), а форматированный текст Ueditor отображается для вторичного редактирования. В настоящее время проект ueditor заархивирован, и соответствующие материалы для реализации этих двух форматов относительно скудны.
`Решение:
1. Загрузите текстовый файл
2. Чтение и создание HTML-файлов в фоновом режиме.
3. Прочитайте содержимое html-файла в фоновом режиме и верните его во внешний интерфейс.
Подарите кому-нибудь розу, оставьте в руке стойкий аромат.
При загрузке файла Word он анализируется и передается во внешний интерфейс через фон.
Структура внутреннего кода:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.15</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.15</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>3.15</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>3.15</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>org.apache.poi.xwpf.converter.xhtml</artifactId>
<version>1.0.6</version>
</dependency>
Код выглядит следующим образом:
/**
* Получить конфигурацию загрузки файла UE
* @param request
* @param response
* @throws IOException
*/
@GetMapping(value = "/config")
public void ueConfig(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
String urlPrefix = ueProperties.getSavepath();
log.info("urlPrefix = "+urlPrefix);
String exec = "{\n" +
" /* Загрузить элементы конфигурации изображения */\n" +
" \"imageActionName\": \"catcherImage\", /* Название действия, которое выполняет загруженное изображение */\n" +
" \"imageFieldName\": \"upfile\", /* Название формы отправленного изображения */\n" +
" \"imageMaxSize\": 2048, /* Ограничение размера загрузки, единица Б */\n" +
" \"imageAllowFiles\": [\".png\", \".jpg\", \".jpeg\", \".gif\", \".bmp\"], /* Загрузить изображение в формате отображения */\n" +
" \"imageCompressEnable\": true, /* Сжимать ли изображения, по умолчанию установлено значение true. */\n" +
" \"imageCompressBorder\": 800, /* Предел сжатия изображения по длинной стороне */\n" +
" \"imageInsertAlign\": \"none\", /* Метод вставленного плавающего изображения */\n" +
" \"imageUrlPrefix\": \"" + urlPrefix + "\", /* Префикс пути доступа к изображению */\n" +
" \"imagePathFormat\": \"/ueditor/image/{yyyy}{mm}{dd}/\", /* Загрузите путь сохранения, вы можете настроить путь сохранения и формат имени файла. */\n" +
" /* {filename} Оно будет заменено исходным именем файла. При настройке необходимо обратить внимание на проблему искажения китайских символов. */\n" +
" /* {rand:6} будет заменено случайным числом, а следующее число — это количество цифр в случайном числе. */\n" +
" /* {time} будет заменен временной меткой */\n" +
" /* {yyyy} будет заменен четырехзначным годом */\n" +
" /* {yy} будет заменен двузначным годом */\n" +
" /* {mm} будет заменено двумя цифрами месяца */\n" +
" /* {dd} будет заменено двузначной датой */\n" +
" /* {hh} будет заменено на два часа */\n" +
" /* {ii} будут заменены двузначными минутами */\n" +
" /* {ss} будет заменено двузначными секундами */\n" +
" /* Недопустимые символы \\ : * ? \" < > | */\n" +
" /* Подробности смотрите в онлайн-документации: fex.baidu.com/ueditor/#use-format_upload_filename */\n" +
"\n" +
" /* Элементы конфигурации загрузки изображений Tuya */\n" +
" \"scrawlActionName\": \"uploadscrawl\", /* Имя действия для выполнения загрузки граффити. */\n" +
" \"scrawlFieldName\": \"upfile\", /* Название формы отправленного изображения */\n" +
" \"scrawlPathFormat\": \"/ueditor/image/{yyyy}{mm}{dd}/\", /* Загрузите путь сохранения, вы можете настроить путь сохранения и формат имени файла. */\n" +
" \"scrawlMaxSize\": 2048000, /* Ограничение размера загрузки, единица Б */\n" +
" \"scrawlUrlPrefix\": \"\", /* Префикс пути доступа к изображению */\n" +
" \"scrawlInsertAlign\": \"none\",\n" +
"\n" +
" /* Загрузка инструмента для создания снимков экрана */\n" +
" \"snapscreenActionName\": \"uploadimage\", /* Название действия, выполняющего загрузку скриншотов */\n" +
" \"snapscreenPathFormat\": \"/ueditor/image/{yyyy}{mm}{dd}/\", /* Загрузите путь сохранения, вы можете настроить путь сохранения и формат имени файла. */\n" +
" \"snapscreenUrlPrefix\": \"\", /* Префикс пути доступа к изображению */\n" +
" \"snapscreenInsertAlign\": \"none\", /* Метод вставленного плавающего изображения */\n" +
"\n" +
" /* Захват конфигурации удаленного образа */\n" +
" \"catcherLocalDomain\": [\"127.0.0.1\", \"localhost\", \"img.baidu.com\"],\n" +
" \"catcherActionName\": \"catchimage\", /* Название действия, используемого для захвата удаленных изображений */\n" +
" \"catcherFieldName\": \"source\", /* Название формы списка отправленных изображений */\n" +
" \"catcherPathFormat\": \"/ueditor/image/{yyyy}{mm}{dd}/\", /* Загрузите путь сохранения, вы можете настроить путь сохранения и формат имени файла. */\n" +
" \"catcherUrlPrefix\": \"" + urlPrefix + "\", /* Префикс пути доступа к изображению */\n" +
" \"catcherMaxSize\": 2048000, /* Ограничение размера загрузки, единица Б */\n" +
" \"catcherAllowFiles\": [\".png\", \".jpg\", \".jpeg\", \".gif\", \".bmp\"], /* Отображение формата захвата изображения */\n" +
"\n" +
" /* Загрузить конфигурацию видео */\n" +
" \"videoActionName\": \"uploadvideo\", /* Имя действия для выполнения загруженного видео. */\n" +
" \"videoFieldName\": \"upfile\", /* Название формы отправленного видео */\n" +
" \"videoPathFormat\": \"/ueditor/video/{yyyy}{mm}{dd}/\", /* Загрузите путь сохранения, вы можете настроить путь сохранения и формат имени файла. */\n" +
" \"videoUrlPrefix\": \"\", /* Префикс пути доступа к видео */\n" +
" \"videoMaxSize\": 10240000, /* Ограничение размера загрузки, единица Б,По умолчанию10 МБ */\n" +
" \"videoAllowFiles\": [\n" +
" \".flv\", \".swf\", \".mkv\", \".avi\", \".rm\", \".rmvb\", \".mpeg\", \".mpg\",\n" +
" \".ogg\", \".ogv\", \".mov\", \".wmv\", \".mp4\", \".webm\", \".mp3\", \".wav\", \".mid\"], /* Загрузить видео в формате отображения */\n" +
" /* Загрузить файл конфигурации */\n" +
" \"fileActionName\": \"uploadfile\",/* В контроллере,Имя действия для выполнения загруженного видео. */\n" +
" \"fileFieldName\": \"upfile\", /* Имя отправленной формы файла */\n" +
" \"filePathFormat\": \"/ueditor/file/{yyyy}{mm}{dd}/\", /* Загрузите путь сохранения, вы можете настроить путь сохранения и формат имени файла. */\n" +
" \"fileUrlPrefix\": \"\", /* Префикс пути доступа к файлу */\n" +
" \"fileMaxSize\": 10240000, /* Ограничение размера загрузки, единица Б,По умолчанию10 МБ */\n" +
" \"fileAllowFiles\": [\n" +
" \".png\", \".jpg\", \".jpeg\", \".gif\", \".bmp\",\n" +
" \".flv\", \".swf\", \".mkv\", \".avi\", \".rm\", \".rmvb\", \".mpeg\", \".mpg\",\n" +
" \".ogg\", \".ogv\", \".mov\", \".wmv\", \".mp4\", \".webm\", \".mp3\", \".wav\", \".mid\",\n" +
" \".rar\", \".zip\", \".tar\", \".gz\", \".7z\", \".bz2\", \".cab\", \".iso\",\n" +
" \".doc\", \".docx\", \".xls\", \".xlsx\", \".ppt\", \".pptx\", \".pdf\", \".txt\", \".md\", \".xml\"\n" +
" ], /* Отображение формата загрузки файла */\n" +
" /* Список изображений в указанном каталоге */\n" +
" \"imageManagerActionName\": \"listimage\", /* Имя действия для управления изображениями */\n" +
" \"imageManagerListPath\": \"/ueditor/image/{yyyy}{mm}{dd}/\", /* Укажите каталог, в котором будут перечислены изображения. */\n" +
" \"imageManagerListSize\": 20, /* Количество файлов, отображаемых каждый раз */\n" +
" \"imageManagerUrlPrefix\": \"" + urlPrefix + "\", /* Префикс пути доступа к изображению */\n" +
" \"imageManagerInsertAlign\": \"none\", /* Метод вставленного плавающего изображения */\n" +
" \"imageManagerAllowFiles\": [\".png\", \".jpg\", \".jpeg\", \".gif\", \".bmp\"], /* Перечисленные типы файлов */\n" +
" /* Список файлов в указанном каталоге */\n" +
" \"fileManagerActionName\": \"listfile\", /* Имя действия для управления файлами */\n" +
" \"fileManagerListPath\": \"/ueditor/file/{yyyy}{mm}{dd}/\", /* Укажите каталог, в котором должны быть перечислены файлы */\n" +
" \"fileManagerUrlPrefix\": \"\", /* Префикс пути доступа к файлу */\n" +
" \"fileManagerListSize\": 20, /* Количество файлов, отображаемых каждый раз */\n" +
" \"fileManagerAllowFiles\": [\n" +
" \".png\", \".jpg\", \".jpeg\", \".gif\", \".bmp\",\n" +
" \".flv\", \".swf\", \".mkv\", \".avi\", \".rm\", \".rmvb\", \".mpeg\", \".mpg\",\n" +
" \".ogg\", \".ogv\", \".mov\", \".wmv\", \".mp4\", \".webm\", \".mp3\", \".wav\", \".mid\",\n" +
" \".rar\", \".zip\", \".tar\", \".gz\", \".7z\", \".bz2\", \".cab\", \".iso\",\n" +
" \".doc\", \".docx\", \".xls\", \".xlsx\", \".ppt\", \".pptx\", \".pdf\", \".txt\", \".md\", \".xml\"\n" +
" ] /* Перечисленные типы файлов */\n" +
"}";
PrintWriter writer = response.getWriter();
writer.write(exec);
writer.flush();
writer.close();
}
Код выглядит следующим образом:
methods: {
ready(editorInstance) {
this.editorInstance=editorInstance
async uploadWordFile(event) {
const file = event.target.files[0];
if (!file) return;
// Преобразование файлов Word в HTML
const htmlContent = await this.convertWordToHtml(file);
const jsonData = JSON.parse(htmlContent)
// Установите содержимое UEditor
console.log(jsonData)
this.editorInstance.execCommand('inserthtml',jsonData.data)
},
async convertWordToHtml(wordFile) {
// Это должен быть код вызова внутреннего интерфейса для преобразования файлов Word в HTML.
// Предположим, есть серверный API, который преобразует Word в HTML.
const formData = new FormData();
formData.append('file', wordFile);
const response = await fetch('/api/ue/uploadFile',{
method:'POST',
body:formData
})
if (response.ok) {
return await response.text();
}
throw new Ошибка('Конвертация не удалась');
}
},
Код выглядит следующим образом:
/** загрузка документа word
*
* @param file
* @return
*/
@PostMapping("/uploadFile")
public Object uploadFile(@RequestParam(name = "file") MultipartFile file){
String filename = file.getOriginalFilename();
JSONObject result = new JSONObject();
String visitHtml = "";
try {
if (filename.endsWith(".docx")) {
//TODO Обработка формата docx
visitHtml = WordConverHtmlUtils.docxToHtmlText(file, ueProperties);
} else if (filename.endsWith(".doc")) {
//TODO Обработка формата документа
visitHtml = WordConverHtmlUtils.docToHtmlText(file, ueProperties);
} else {
log.error("Неподдерживаемый формат файла!");
}
result.put("state", "SUCCESS");
result.put("data", visitHtml);
log.info("result: {}", result.toString());
} catch (Exception e) {
log.error("Исключение: файл не найден!");
e.printStackTrace();
}
return result;
}
**⚠️⚠️⚠️⚠️⚠️
options.URIResolver(new BasicURIResolver(picUri));
Необходимо установить адрес изображения, к которому внешний интерфейс может получить прямой доступ, например, автора: http://localhost:8000/resource/ueditor/file/20240404/1712220732312.png,
⚠️⚠️⚠️⚠️⚠️ В противном случае редактор ueditor не сможет отобразить картинки в документе Word**
Код выглядит следующим образом:
package com.ue.demo.utils;
import cn.hutool.core.lang.UUID;
import com.ue.demo.config.UeProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.converter.PicturesManager;
import org.apache.poi.hwpf.converter.WordToHtmlConverter;
import org.apache.poi.hwpf.usermodel.PictureType;
import org.apache.poi.xwpf.converter.core.BasicURIResolver;
import org.apache.poi.xwpf.converter.core.FileImageExtractor;
import org.apache.poi.xwpf.converter.xhtml.XHTMLConverter;
import org.apache.poi.xwpf.converter.xhtml.XHTMLOptions;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.web.multipart.MultipartFile;
import org.w3c.dom.Document;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* @author:Peanut
* @create: 2024-04-05 10:22
* @version: 1.0.0
* @description:
*/
@Slf4j
public class WordConverHtmlUtils {
private final static String FILE_URL_PRE = "/ueditor/file/";
/**
* Загрузите документ docx и верните проанализированный Html.
*/
public static String docxToHtmlText(MultipartFile file, UeProperties ueProperties) throws Exception {
try {
String fileName = UUID.fastUUID().toString();
//адрес хранения изображений
String imagePath = ueProperties.getSavepath().concat(FILE_URL_PRE).concat("/");
String fileOutName = imagePath.concat(fileName).concat(".html");
log.info("Загрузить анализ документа docx");
log.info("Загрузите документ docx и верните проанализированный Html., imagePath:{}", imagePath);
log.info("fileOutName:{}", fileOutName);
//Получаем объект, используемый для работы с Word
XWPFDocument document = new XWPFDocument(file.getInputStream());
//Некоторые основные классы настроек при экспорте в html
XHTMLOptions options = null;
//Определяем, есть ли картинки в вордовом файле
if(document.getAllPictures().size() > 0) {
//Получаем объект по умолчанию и устанавливаем отступ
options = XHTMLOptions.getDefault().indent(4);
// Если изображения включены, установите местоположение экспорта изображений.
File imageFolder = new File(imagePath);
//Установим папку назначения для экстрактора изображений Используется для хранения файлов изображений
options.setExtractor(new FileImageExtractor(imageFolder));
// URI resolver Путь к каталогу изображений в HTML HTML
//⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️
//⚠️⚠️⚠️⚠️⚠️ Необходимо установить адрес изображения, к которому внешний интерфейс может получить прямой доступ, например, автора: http://localhost:8000/resource/ueditor/file/20240404/1712220732312.png,
//⚠️⚠️⚠️⚠️⚠️ В противном случае редактор ueditor не сможет отобразить картинки в документе Word.
String picUri = ueProperties.getShowpath().concat(imagePath.substring(imagePath.indexOf("ueditor")));
options.URIResolver(new BasicURIResolver(picUri));
}
//Получаем выходной объект HTML-файла
File outFile = new File(fileOutName);
if(!outFile.getParentFile().exists()){
outFile.getParentFile().mkdirs();
}
//Создаем все родительские пути, если родительский каталог не существует
outFile.getParentFile().mkdirs();
//Создаем выходной поток
OutputStream out = new FileOutputStream(outFile);
//html-конвертер
XHTMLConverter.getInstance().convert(document, out, options);
log.info("HTML-конвертер success");
//Обрабатываем сгенерированный HTML-код и передаем его интерфейсу в виде строки
return readHtmlStr(fileOutName);
} catch (Exception e) {
log.error("docxToHtmlText Исключение синтаксического анализа", e);
}
return "";
}
/**
* Загрузите документ Word в формате doc и верните проанализированный Html.
* @param file
* @param ueProperties
* @return
* @throws Exception
*/
public static String docToHtmlText(MultipartFile file, UeProperties ueProperties) throws Exception {
//Используем поток массива символов для получения проанализированного содержимого
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStream outStream = new BufferedOutputStream(baos);
try {
String fileName = UUID.fastUUID().toString();
//Передаем загруженный файл в преобразование документов
//адрес хранения изображений
String docPath = ueProperties.getSavepath().concat(FILE_URL_PRE).concat("/");
String imagePath = docPath.concat("image/");
String fileOutName = docPath.concat(fileName).concat(".html");
log.info("Загрузить документ, вернуть анализ ");
log.info("fileOutName:{}", fileOutName);
//Создаем каталог для хранения файлов изображений
new File(imagePath).mkdirs();
//Класс сущности, соответствующий документу в poi
HWPFDocument hwpfDocument = new HWPFDocument(file.getInputStream());
//Создаем объект преобразования, используя пустой объект документа
WordToHtmlConverter converter = new WordToHtmlConverter(DocumentBuilderFactory
.newInstance()
.newDocumentBuilder()
.newDocument());
//Устанавливаем менеджер для хранения картинок, реализованный с использованием анонимных внутренних классов Этот класс реализует интерфейс PicturesManager и метод savePicture.
converter.setPicturesManager(new PicturesManager() {
FileOutputStream out = null;
//Этот метод будет вызываться внутри метода ProcessDocument ниже Используется для хранения файлов изображений в Word.
@Override
public String savePicture(byte[] bytes, PictureType pictureType, String name, float width, float height) {
try {
//Сохраняем одну фотографию
out = new FileOutputStream(imagePath + name);
out.write(bytes);
} catch (IOException exception) {
exception.printStackTrace();
}finally {
if(out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//Здесь нам нужно вернуть сохраненный путь оператору (HtmlDocumentFacade) Используется для поиска ресурсов изображения при создании Html.
//⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️
//⚠️⚠️⚠️⚠️⚠️ Необходимо установить адрес изображения, к которому внешний интерфейс может получить прямой доступ, например, автора: http://localhost:8000/resource/ueditor/file/20240404/1712220732312.png,
//⚠️⚠️⚠️⚠️⚠️ В противном случае редактор ueditor не сможет отобразить картинки в документе Word.
return ueProperties.getShowpath().concat(imagePath.substring(imagePath.indexOf("ueditor"))).concat(name);
}
});
//Используем режим внешнего вида, чтобы установить для объекта документа hwpfDocument свойство Document в HtmlDocumentFacade
converter.processDocument(hwpfDocument);
//Получаем документ document в конвертере
Document htmlDocument = converter.getDocument();
//Действует как объектная модель документа (DOM) Владелец преобразованного исходного дерева в форме дерева. -- исходное дерево
DOMSource domSource = new DOMSource(htmlDocument);
//конвертер Этот объект используется для конвертации исходного дерево Преобразовать в дерево результатов
Transformer transformer = TransformerFactory.newInstance().newTransformer();
//Установим метод вывода, который также можно назвать типом файла результирующего дерева. Это может быть html/xml/text или какой-либо тип расширения, расширяющий первые три.
transformer.setOutputProperty(OutputKeys.METHOD , "html");
//Установим некоторые необходимые атрибуты Установите кодировку при выводе в utf-8.
transformer.setOutputProperty(OutputKeys.ENCODING , "utf-8");
//Конвертировать Введите исходное дерево Преобразовать в дерево результатови вывести вstreamResultсередина
transformer.transform(domSource , new StreamResult(new File(fileOutName)));
log.info("HTML-конвертер success");
//Обрабатываем сгенерированный HTML-код и передаем его интерфейсу в виде строки
return readHtmlStr(fileOutName);
} catch (Exception e) {
log.error("docToHtmlText аномальный", e);
} finally {
baos.close();
outStream.close();
}
return null;
}
/**
* Прочтите html-файл, преобразуйте его в строку и верните во внешний интерфейс.
* Удалить разрывы строк и два последовательных пробела
* @param htmlDirPath путь к html-файлу
* @return
* @throws IOException
*/
private static String readHtmlStr(String htmlDirPath) throws IOException {
log.info("Обработайте сгенерированный HTML-код и передайте его интерфейсу в виде строки: {} ...Start..", htmlDirPath);
String htmlStr = "";
try {
Path htmlPath = Paths.get(htmlDirPath);
htmlStr = new String(Files.readAllBytes(htmlPath));
htmlStr = htmlStr.replaceAll("\\n", "");
htmlStr = htmlStr.replaceAll("\\s{2,}", " ");
log.info("Обработать сгенерированный HTML-код и передать его интерфейсу в виде строки...end");
} catch (IOException e) {
log.error("При обработке сгенерированного HTML-кода в строковой форме произошла ошибка, {}", e.getMessage());
}
return htmlStr;
}
}
Код выглядит следующим образом:
spring.application.name=ue
server.port=8000
##Конфигурация редактора UE
#Editor обращается к адресу изображения сервера
ue.showpath=http://localhost:8000/resource
#ue префикс пути к хранилищу файлов
ue.savepath=/Users/cookie/Documents/coding/uedemo
!!!ue.showpath=Если у вас работает nginx, вам необходимо настроить его в nginx.conf
Подарите кому-нибудь розу, оставьте в руке стойкий аромат.
Адрес исходного кода:
https://gitee.com/gwancookie/uedemo
Прочтите текстовый документ, чтобы создать ссылку в формате HTML: