Na indústria automotiva, a necessidade de gerenciar documentação técnica em formatos do Word é comum. Muitas vezes, engenheiros e técnicos precisam colar conteúdo diretamente de documentos do Word em editores online, mantendo a formatação e tratando as imagens adequadamente.
Desafio Técnico
O editor WANGEDITOR, embora poderoso, não oferece suporte nativo para colar conteúdo do Word com todas as imagens automaticamente enviadas para o servidor. Este desafio se torna particularmente crítico em ambientes onde a docuemntação técnica contém numerosas imagens e diagramas.
Solução Proposta
A solução envolve a criação de um plugin personalizado que:
- Intercepta o evento de colagem
- Processa o HTML do Word
- Extrai todas as imagens embutidas
- Envia as imagens para o servidor
- Substitui os URLs base64 pelos URLs das imagens no servidor
Implementação
Configuração Básica do Editor
// config/editor.js
import { Editor } from 'wangeditor'
import { Boot } from '@wangeditor/core'
// Registro do módulo de processamento de colagem
Boot.registerModule({
name: 'wordPasteHandler',
editor: Editor,
config: (editor) => {
editor.on('paste', async (event) => {
const { type, data } = event
if (type === 'text/html') {
// Processar HTML do Word
const processedContent = await processWordHTML(data)
event.preventDefault()
// Inserir conteúdo processado
editor.insertHtml(processedContent)
}
})
}
})
async function processWordHTML(html) {
// Extrair e processar imagens
const imageProcessor = new ImageProcessor()
const { processedHTML, imageUrls } = await imageProcessor.extractAndUploadImages(html)
return processedHTML
}
Processamento de Imagens
// utils/imageProcessor.js
export class ImageProcessor {
constructor() {
this.uploadEndpoint = '/api/upload-images'
this.imageContainer = []
}
async extractAndUploadImages(html) {
// Usar DOMParser para analisar HTML
const parser = new DOMParser()
const doc = parser.parseFromString(html, 'text/html')
// Encontrar todas as imagens
const images = Array.from(doc.querySelectorAll('img'))
const processedImages = []
for (const img of images) {
const src = img.getAttribute('src')
if (src.startsWith('data:image')) {
// Converter base64 para blob
const blob = this.base64ToBlob(src)
// Enviar para servidor
const serverUrl = await this.uploadImage(blob)
// Substituir URL
img.setAttribute('src', serverUrl)
processedImages.push(img.outerHTML)
} else {
processedImages.push(img.outerHTML)
}
}
// Substituir imagens no HTML
const processedHTML = html.replace(/<img></img>]+>/g, () => {
return processedImages.shift()
})
return {
processedHTML,
imageUrls: processedImages.map(img => img.match(/src="([^"]+)"/)[1])
}
}
base64ToBlob(base64) {
const arr = base64.split(',')
const mime = arr[0].match(/:(.*?);/)[1]
const bstr = atob(arr[1])
let n = bstr.length
const u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new Blob([u8arr], { type: mime })
}
async uploadImage(blob) {
const formData = new FormData()
formData.append('image', blob, `image_${Date.now()}.${this.getFileExtension(blob)}`)
const response = await fetch(this.uploadEndpoint, {
method: 'POST',
body: formData
})
const result = await response.json()
return result.url
}
getFileExtension(blob) {
const mime = blob.type
switch (mime) {
case 'image/jpeg': return 'jpg'
case 'image/png': return 'png'
case 'image/gif': return 'gif'
case 'image/webp': return 'webp'
default: return 'png'
}
}
}
Backend para Upload de Imagens
// routes/upload.js
const express = require('express')
const multer = require('multer')
const path = require('path')
const router = express.Router()
// Configuração do multer para armazenamento
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'public/uploads/')
},
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9)
cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname))
}
})
const upload = multer({
storage: storage,
limits: { fileSize: 5 * 1024 * 1024 }, // 5MB
fileFilter: (req, file, cb) => {
const allowedTypes = /jpeg|jpg|png|gif|webp/
const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase())
const mimetype = allowedTypes.test(file.mimetype)
if (mimetype && extname) {
return cb(null, true)
} else {
cb(new Error('Apenas imagens são permitidas!'))
}
}
})
// Rota de upload
router.post('/upload-images', upload.single('image'), (req, res) => {
if (!req.file) {
return res.status(400).json({ error: 'Nenhum arquivo foi enviado' })
}
const imageUrl = `/uploads/${req.file.filename}`
res.json({
success: true,
url: imageUrl,
filename: req.file.filename
})
})
// Tratamento de erros
router.use((err, req, res, next) => {
if (err instanceof multer.MulterError) {
if (err.code === 'LIMIT_FILE_SIZE') {
return res.status(400).json({ error: 'O tamanho do arquivo excede o limite de 5MB' })
}
} else if (err) {
return res.status(400).json({ error: err.message })
}
next()
})
module.exports = router
Integração com Vue.js
// components/WordEditor.vue
<template>
<div class="word-editor-container">
<div ref="editorElem" style="height: 500px;"></div>
</div>
</template>
<script>
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { Boot } from '@wangeditor/core'
import { ImageProcessor } from '@/utils/imageProcessor'
export default {
components: { Editor, Toolbar },
data() {
return {
editor: null,
editorConfig: {
placeholder: 'Cole conteúdo do Word aqui...',
MENU_CONF: {
uploadImage: {
server: '/api/upload-images',
fieldName: 'image',
maxFileSize: 5 * 1024 * 1024,
allowedFileTypes: ['image/*'],
customInsert: (res, insertFn) => {
insertFn(res.url)
}
}
}
},
toolbarConfig: {
excludeKeys: [
'uploadVideo'
]
}
}
},
mounted() {
this.initEditor()
},
beforeDestroy() {
const editor = this.editor
if (editor == null) return
editor.destroy()
this.editor = null
},
methods: {
initEditor() {
// Criar instância do editor
this.editor = new Editor({
selector: this.$refs.editorElem,
config: this.editorConfig,
toolbarConfig: this.toolbarConfig
})
// Registrar módulo de processamento de colagem
Boot.registerModule({
name: 'wordPasteHandler',
editor: this.editor.constructor,
config: (editor) => {
editor.on('paste', async (event) => {
const { type, data } = event
if (type === 'text/html') {
// Processar HTML do Word
const imageProcessor = new ImageProcessor()
const { processedHTML } = await imageProcessor.extractAndUploadImages(data)
event.preventDefault()
// Inserir conteúdo processado
editor.insertHtml(processedHTML)
}
})
}
})
// Criar editor
this.editor.create()
}
}
}
</script>
<style>
.word-editor-container {
border: 1px solid #ccc;
z-index: 100;
position: relative;
}
</style>
Considerações de Desempenho
Para documentos do Word com muitas imagens, é importante cosniderar:
- Implementar compressão de imagens no frontend antes do upload
- Usar upload em chunks para arquivos grandes
- Implementar uma fila de processamento para lidar com múltiplos uploads simultâneos
- Adicionar indicadores de progresso para melher experiência do usuário
Extensões para Formatos Adicionais
Para ambientes da indústria automotiva que lidam com diversos formatos de documentos, é possível estender a solução para:
- PDFs: converter páginas em imagens
- Arquivos Excel: extrair gráficos como imagens
- Apresentações PowerPoint: converter slides em imagens
Segurança
É fundamental implementar medidas de segurança:
- Validação rigorosa de todos os uploads
- Escaneamento de malware em arquivos enviados
- Limitação de tipos de arquivos permitidos
- Implementação de rate limiting para evitar abusos
Conclusão
A implementação de suporte à colagem de documentos do Word no WANGEDITOR, com processamento automático de imagens, oferece uma solução robusta para ambientes que gerenciam documentação técnica. A abordagem modular permite extensibilidade para outros formatos e garantida segurança contra ameaças comuns.