Em um projeto recente, enfrentamos o desafio de:
Para projetos frontend fornecidos por terceiros, contendo CSS, JS, HTML, imagens e outros recursos, precisávamos armazenar esses projetos em um banco de dados e exibi-los em um iframe. Além disso, os recursos referenciados pelo HTML (como scripts e folhas de estilo) também precisavam ser carregados coretamente a partir do banco de dados.
A abordagem foi dividida em duas etapas:
- Amrazenar todos os arquivos do projeto (JS, CSS, HTML, imagens, etc.) no banco de dados, mantendo suas referências de origem;
- Exibir o HTML principal em um iframe, garantindo que os recursos referenciados sejam carregados corretamente do banco de dados.
Este artigo foca na segunda etapa, detalhando o processo e a implementação.
Consideremos um exemplo de projeto frontend que precisa ser exibido:
O projeto contém vários arquivos, incluindo HTML, CSS, JavaScript e imagens. Quando aberto diretamente, o projeto exibe corretamente. Nosso objetivo é armazenar esses arquivos em um banco de dados e exibi-los em um iframe em nossa aplicação JSP.
- Estrutura da tabela do banco de dados:
Para armazenar os arquivos do projeto, criamos a seguinte tabela:
create table t_project_files(
project_name varchar2(200),
resource_path varchar2(500),
file_content blob
);
Neste exemplo, todos os arquivos do projeto já foram armazenados corretamente no banco de dados.
- Implementação do JSP com iframe:
No arquivo displayProject.jsp, criamos um iframe que referencia o arquivo HTML principal do projeto:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Visualização de Projeto em Iframe</title>
<style>
#contentFrame{
width: 100%;
height: 500px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<center>Visualização do projeto em iframe, recursos carregados do banco de dados</center>
<iframe id="contentFrame" src="${pageContext.request.contextPath}/project/demo.zip/mobile_nav.html"/>
</body>
</html>
- Lógica de implementação:
A solução funciona da seguinte forma:
- O src do iframe utiliza um padrão de URL: /project/nomeDoProjeto/caminhoDoArquivo
- O controlador Spring intercepta essas requisições, extrai o nome do projeto e o caminho do recurso
- O serviço busca o conteúdo do arquivo no banco de dados com base nesses parâmetros
- A resposta HTTP é configurada com o Content-Type apropriado para cada tipo de arquivo
- Um ponto crucial é que os recursos referenciados no HTML também usam o mesmo padrão de URL, permitindo que sejam carrregados corretamente do banco de dados
- Controlador Spring (ProjectController):
package com.example.controller;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.HandlerMapping;
import com.example.service.ProjectService;
/**
* Controlador para exibição de projetos em iframes
*/
@Controller
public class ProjectController {
private static Logger logger = Logger.getLogger(ProjectController.class);
@Autowired
private ProjectService projectService;
/**
* Processa requisições para recursos de projetos
*/
@RequestMapping("/project/{projectName}/**")
public void serveProjectResource(@PathVariable("projectName") String projectName,
HttpServletResponse response, HttpServletRequest request)
{
String resourcePath = extractPathFromPattern(request);
logger.info("Nome do projeto: "+projectName);
logger.info("Caminho do recurso: "+resourcePath);
byte[] resourceContent = projectService.getResourceContent(projectName, resourcePath);
String contentType = projectService.getContentType(resourcePath);
response.setContentType(contentType);
response.setCharacterEncoding("utf-8");
if(resourceContent != null){
try{
InputStream resourceStream = new ByteArrayInputStream(resourceContent);
OutputStream outputStream = response.getOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while((bytesRead = resourceStream.read(buffer)) != -1){
outputStream.write(buffer, 0, bytesRead);
}
outputStream.close();
resourceStream.close();
}catch (IOException e) {
logger.error("Erro ao servir recurso do projeto", e);
}
}
}
// Extrai o caminho do recurso do padrão da URL
private static String extractPathFromPattern(final HttpServletRequest request) {
String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
return new AntPathMatcher().extractPathWithinPattern(bestMatchPattern, path);
}
}
- Serviço de projeto (ProjectService):
package com.example.service;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.dao.ProjectResourceMapper;
import com.example.model.ProjectResource;
@Service
public class ProjectService {
@Autowired
private ProjectResourceMapper resourceMapper;
/**
* Obtém o conteúdo de um recurso do projeto
*/
public byte[] getResourceContent(String projectName, String resourcePath){
ProjectResource resource = resourceMapper.getResourceContent(projectName, resourcePath);
return resource != null ? resource.getFileContent() : null;
}
/**
* Determina o Content-Type com base na extensão do arquivo
*/
public String getContentType(String resourcePath){
int extensionPos = resourcePath.lastIndexOf(".");
String extension = extensionPos >= 0 ? resourcePath.substring(extensionPos + 1) : "";
String contentType = "application/octet-stream"; // Tipo padrão para binários
Map<String, String> typeMap = new HashMap<String, String>() {{
put("html", "text/html");
put("htm", "text/html");
put("jpg", "image/jpeg");
put("jpeg", "image/jpeg");
put("png", "image/png");
put("gif", "image/gif");
put("css", "text/css");
put("js", "application/javascript");
put("json", "application/json");
put("svg", "image/svg+xml");
}};
return typeMap.getOrDefault(extension.toLowerCase(), contentType);
}
}
- Mapper de recursos (ProjectResourceMapper):
package com.example.dao;
import org.apache.ibatis.annotations.Param;
import com.example.model.ProjectResource;
public interface ProjectResourceMapper {
ProjectResource getResourceContent(@Param("projectName")String projectName,
@Param("resourcePath")String resourcePath);
}
- XML do Mapper (ProjectResourceMapper.xml):
<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.example.dao.ProjectResourceMapper" >
<select id="getResourceContent" resultType="com.example.model.ProjectResource">
select * from t_project_files
where project_name=#{projectName} and resource_path=#{resourcePath}
</select>
</mapper>
- Modelo de recurso (ProjectResource.java):
package com.example.model;
/**
* Representa um arquivo de projeto armazenado no banco de dados
*/
public class ProjectResource {
private String projectName;
private String resourcePath;
private byte[] fileContent;
// Getters e Setters
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getResourcePath() {
return resourcePath;
}
public void setResourcePath(String resourcePath) {
this.resourcePath = resourcePath;
}
public byte[] getFileContent() {
return fileContent;
}
public void setFileContent(byte[] fileContent) {
this.fileContent = fileContent;
}
}
Com essa implementação, ao acessar a URL da página JSP, o iframe exibirá corretamente o projeto, com todos os seus recursos (CSS, JS, imagens, etc.) sendo carregados do banco de dados.