This commit is contained in:
manalejandro 2018-07-15 03:45:44 +02:00
parent bb3ccad495
commit 236e3b4047
13 changed files with 446 additions and 98 deletions

View File

@ -6,7 +6,7 @@
<groupId>com.manalejandro</groupId> <groupId>com.manalejandro</groupId>
<artifactId>arjion</artifactId> <artifactId>arjion</artifactId>
<version>0.1.0-SNAPSHOT</version> <version>0.2.0-SNAPSHOT</version>
<packaging>war</packaging> <packaging>war</packaging>
<name>arjion</name> <name>arjion</name>

View File

@ -1,13 +1,20 @@
package com.manalejandro.arjion.controllers; package com.manalejandro.arjion.controllers;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.text.Normalizer; import java.text.Normalizer;
import java.util.ArrayList; import java.util.ArrayList;
import javax.servlet.http.HttpServletResponse;
import com.manalejandro.arjion.model.Archivo; import com.manalejandro.arjion.model.Archivo;
import com.manalejandro.arjion.model.Documento;
import com.manalejandro.arjion.services.MainService;
import com.manalejandro.arjion.vo.DetailVO;
import com.manalejandro.arjion.vo.DocumentoVO; import com.manalejandro.arjion.vo.DocumentoVO;
import org.apache.tika.config.TikaConfig; import org.apache.tika.config.TikaConfig;
@ -18,7 +25,12 @@ import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.AutoDetectParser; import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.ParseContext; import org.apache.tika.parser.ParseContext;
import org.apache.tika.sax.BodyContentHandler; import org.apache.tika.sax.BodyContentHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -32,12 +44,21 @@ import org.xml.sax.SAXException;
@Controller @Controller
public class MainController { public class MainController {
private final MainService mainService;
@Value("${arjion.uploadpath}") @Value("${arjion.uploadpath}")
private String uploadpath; private String uploadpath;
@Autowired
public MainController(MainService mainService) {
this.mainService = mainService;
}
@RequestMapping(path = "/") @RequestMapping(path = "/")
public String indexPage(final Model model) { public String indexPage(final Model model) {
DocumentoVO documentoVO = new DocumentoVO(); DocumentoVO documentoVO = new DocumentoVO();
documentoVO.setCount(mainService.count());
documentoVO.setDocumentos(mainService.findAllDocumento());
model.addAttribute("documentoVO", documentoVO); model.addAttribute("documentoVO", documentoVO);
return "index"; return "index";
} }
@ -51,24 +72,67 @@ public class MainController {
public String uploadPage(final Model model, @RequestParam("archivos") MultipartFile[] archivos) public String uploadPage(final Model model, @RequestParam("archivos") MultipartFile[] archivos)
throws IOException, TikaException, SAXException { throws IOException, TikaException, SAXException {
DocumentoVO documentoVO = new DocumentoVO(); DocumentoVO documentoVO = new DocumentoVO();
documentoVO.setArchivos(new ArrayList<Archivo>()); documentoVO.setCount(mainService.count());
TikaConfig tikaConfig = TikaConfig.getDefaultConfig(); documentoVO.setDocumentos(mainService.findAllDocumento());
for (int i = 0; i < archivos.length; i++) { if (archivos.length > 0) {
byte[] bytes = archivos[i].getBytes(); documentoVO.setArchivos(new ArrayList<Archivo>());
String normalized = Normalizer.normalize(archivos[i].getOriginalFilename(), Normalizer.Form.NFD), // Recupera la conficuración de Tika
filename = normalized.replaceAll("\\p{InCombiningDiacriticalMarks}+", ""); TikaConfig tikaConfig = TikaConfig.getDefaultConfig();
Path path = Paths.get(uploadpath + filename); // Itera los archivos recibidos
Files.write(path, bytes); for (int i = 0; i < archivos.length; i++) {
Metadata metadata = new Metadata(); byte[] bytes = archivos[i].getBytes();
AutoDetectParser parser = new AutoDetectParser(tikaConfig); // Normaliza el título de los archivos
ContentHandler handler = new BodyContentHandler(-1); String normalized = Normalizer.normalize(archivos[i].getOriginalFilename(), Normalizer.Form.NFD),
TikaInputStream stream = TikaInputStream.get(bytes); filename = normalized.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
parser.parse(stream, handler, metadata, new ParseContext()); Path path = Paths.get(uploadpath + filename);
LanguageIdentifier identifier = new LanguageIdentifier(handler.toString()); // Instancias necesarias
documentoVO.getArchivos().add(new Archivo(filename, String.valueOf(archivos[i].getSize()), Metadata metadata = new Metadata();
metadata.toString(), handler.toString(), identifier.getLanguage())); AutoDetectParser parser = new AutoDetectParser(tikaConfig);
// Usa -1 para no tener límite de 100000 chars
ContentHandler handler = new BodyContentHandler(-1);
// Castea los bytes al Stream de Tika
TikaInputStream stream = TikaInputStream.get(bytes);
// Parsea el contenido
parser.parse(stream, handler, metadata, new ParseContext());
// Identifica el idioma del archivo
LanguageIdentifier identifier = new LanguageIdentifier(handler.toString());
// Almacena en elasticsearch
if (!mainService.save(new Documento(filename, Long.valueOf(archivos[i].getSize()).intValue(),
metadata.toString(), handler.toString(), identifier.getLanguage()))) {
return "exists";
} else {
// Guarda el archivo en el directorio configurado en las properties
Files.write(path, bytes);
}
// Añade los parámetros al VO para mostrar en la vista
documentoVO.getArchivos().add(new Archivo(filename, String.valueOf(archivos[i].getSize()),
metadata.toString(), handler.toString(), identifier.getLanguage()));
}
} }
model.addAttribute("documentoVO", documentoVO); model.addAttribute("documentoVO", documentoVO);
return "index"; return "index";
} }
@GetMapping(path = "/detail")
public String detail(final Model model, @RequestParam(value = "nombre", required = true) String nombre) {
DetailVO detailVO = new DetailVO();
detailVO.setDocumento(mainService.findOne(nombre));
model.addAttribute("detailVO", detailVO);
return "detail";
}
@GetMapping(path = "/download")
public ResponseEntity<ByteArrayResource> download(final HttpServletResponse response,
@RequestParam(value = "filename", required = true) String filename)
throws IOException, MalformedURLException {
File file = new File(uploadpath + filename);
Path path = Paths.get(file.getAbsolutePath());
ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(path));
String type = file.toURL().openConnection().guessContentTypeFromName(filename);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add("content-disposition", "attachment; filename=" + filename);
responseHeaders.add("Content-Type", type);
return ResponseEntity.ok().contentLength(file.length()).headers(responseHeaders)
.contentType(MediaType.parseMediaType("application/octet-stream")).body(resource);
}
} }

View File

@ -12,21 +12,97 @@ import com.fasterxml.jackson.annotation.JsonProperty;
@Setting(settingPath = "/elasticsearch/settings.json") @Setting(settingPath = "/elasticsearch/settings.json")
@Mapping(mappingPath = "/elasticsearch/mapping.json") @Mapping(mappingPath = "/elasticsearch/mapping.json")
public class Documento { public class Documento {
@Id @Id
public Integer id; public String nombre;
public Integer tamano;
public String metadata;
public String contenido;
public String lenguaje;
@JsonCreator @JsonCreator
public Documento(@JsonProperty("id") Integer id) { public Documento(@JsonProperty("nombre") String nombre, @JsonProperty("tamano") Integer tamano,
super(); @JsonProperty("metadata") String metadata, @JsonProperty("contenido") String contenido,
this.id = id; @JsonProperty("lenguaje") String lenguaje) {
} super();
this.nombre = nombre;
this.tamano = tamano;
this.metadata = metadata;
this.contenido = contenido;
this.lenguaje = lenguaje;
}
@JsonProperty("id") /**
public Integer getId() { * @return the nombre
return id; */
} @JsonProperty("nombre")
public String getNombre() {
return nombre;
}
public void setId(Integer id) { /**
this.id = id; * @param nombre the nombre to set
} */
public void setNombre(String nombre) {
this.nombre = nombre;
}
/**
* @return the tamano
*/
@JsonProperty("tamano")
public Integer getTamano() {
return tamano;
}
/**
* @param tamano the tamano to set
*/
public void setTamano(Integer tamano) {
this.tamano = tamano;
}
/**
* @return the metadata
*/
@JsonProperty("metadata")
public String getMetadata() {
return metadata;
}
/**
* @param metadata the metadata to set
*/
public void setMetadata(String metadata) {
this.metadata = metadata;
}
/**
* @return the contenido
*/
@JsonProperty("contenido")
public String getContenido() {
return contenido;
}
/**
* @param contenido the contenido to set
*/
public void setContenido(String contenido) {
this.contenido = contenido;
}
/**
* @return the lenguaje
*/
@JsonProperty("lenguaje")
public String getLenguaje() {
return lenguaje;
}
/**
* @param lenguaje the lenguaje to set
*/
public void setLenguaje(String lenguaje) {
this.lenguaje = lenguaje;
}
} }

View File

@ -1,8 +1,16 @@
package com.manalejandro.arjion.services; package com.manalejandro.arjion.services;
import org.springframework.stereotype.Service; import java.util.List;
import com.manalejandro.arjion.model.Documento;
@Service
public interface MainService { public interface MainService {
public boolean save(Documento doc);
public long count();
public List<Documento> findAllDocumento();
public Documento findOne(String nombre);
} }

View File

@ -1,5 +1,51 @@
package com.manalejandro.arjion.services; package com.manalejandro.arjion.services;
import java.util.ArrayList;
import java.util.List;
import com.manalejandro.arjion.model.Documento;
import com.manalejandro.arjion.repositories.MainRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MainServiceImpl implements MainService { public class MainServiceImpl implements MainService {
private final MainRepository mainRepository;
@Autowired
public MainServiceImpl(MainRepository mainRepository) {
this.mainRepository = mainRepository;
}
@Override
public boolean save(Documento doc) {
if (!mainRepository.existsById(doc.nombre)) {
if (mainRepository.save(doc) != null)
return true;
else
return false;
} else
return false;
}
@Override
public long count() {
return mainRepository.count();
}
@Override
public List<Documento> findAllDocumento() {
List<Documento> docs = new ArrayList<Documento>();
mainRepository.findAll().forEach(doc -> {
docs.add((Documento) doc);
});
return docs;
}
@Override
public Documento findOne(String nombre) {
return mainRepository.findById(nombre).get();
}
} }

View File

@ -0,0 +1,22 @@
package com.manalejandro.arjion.vo;
import com.manalejandro.arjion.model.Documento;
public class DetailVO {
private Documento documento;
/**
* @return the documento
*/
public Documento getDocumento() {
return documento;
}
/**
* @param documento the documento to set
*/
public void setDocumento(Documento documento) {
this.documento = documento;
}
}

View File

@ -3,9 +3,13 @@ package com.manalejandro.arjion.vo;
import java.util.List; import java.util.List;
import com.manalejandro.arjion.model.Archivo; import com.manalejandro.arjion.model.Archivo;
import com.manalejandro.arjion.model.Documento;
public class DocumentoVO { public class DocumentoVO {
private List<Archivo> archivos; private List<Archivo> archivos;
private long count;
private List<Documento> documentos;
/** /**
* @return the archivos * @return the archivos
@ -20,4 +24,26 @@ public class DocumentoVO {
public void setArchivos(List<Archivo> archivos) { public void setArchivos(List<Archivo> archivos) {
this.archivos = archivos; this.archivos = archivos;
} }
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
/**
* @return the documentos
*/
public List<Documento> getDocumentos() {
return documentos;
}
/**
* @param documentos the documentos to set
*/
public void setDocumentos(List<Documento> documentos) {
this.documentos = documentos;
}
} }

View File

@ -12,5 +12,5 @@ spring.thymeleaf.enabled=true
spring.thymeleaf.prefix=classpath:/templates/ spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false spring.thymeleaf.cache=false
spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-file-size=20MB
spring.servlet.multipart.max-request-size=20MB spring.servlet.multipart.max-request-size=100MB

View File

@ -1,16 +1,28 @@
{ {
"documento": { "documento": {
"properties": { "properties": {
"@timestamp": { "@timestamp": {
"type": "date", "type": "date",
"format": "strict_date_optional_time||epoch_millis" "format": "strict_date_optional_time||epoch_millis"
}, },
"@version": { "@version": {
"type": "keyword" "type": "keyword"
}, },
"id": { "nombre": {
"type": "long" "type": "text"
} },
} "tamano": {
} "type": "long"
},
"metadata": {
"type": "text"
},
"contenido": {
"type": "text"
},
"lenguaje": {
"type": "keyword"
}
}
}
} }

View File

@ -0,0 +1,4 @@
hr {
width: 100%;
text-align: center;
}

View File

@ -0,0 +1,46 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Arjion</title>
<link rel="stylesheet" th:href="@{/webjars/bootstrap/3.3.7-1/css/bootstrap.min.css}">
<link rel="stylesheet" th:href="@{/css/main.css}">
<script th:src="@{/webjars/jquery/3.1.1-1/jquery.min.js}"></script>
<script th:src="@{/webjars/bootstrap/3.3.7-1/js/bootstrap.min.js}"></script>
<script th:src="@{/js/main.js}"></script>
</head>
<body>
<header class="text-center">
<a th:href="@{/}">
<h1 class="text-primary">Arjion</h1>
</a>
<h3 class="text-warning">[[${detailVO.documento.nombre}]]</h3>
</header>
<section class="col-md-12">
<hr>
</section>
<section>
<span class="col-md-1 text-muted">Tamaño</span>
<span class="col-md-11 text-muted">[[${detailVO.documento.tamano}]] bytes</span>
<span class="col-md-1 text-muted">Lenguaje</span>
<span class="col-md-11 text-muted">[[${detailVO.documento.lenguaje}]]</span>
<span class="col-md-1 text-success">Metadatos</span>
<span class="col-md-11 text-success">[[${detailVO.documento.metadata}]]</span>
<span class="col-md-1 text-warning">Contenido</span>
<pre class="col-md-11 text-warning">[[${detailVO.documento.contenido}]]</pre>
</div>
</section>
<section class="col-md-12">
<hr>
</section>
<footer class="col-md-12 text-center">
<span class="col-md-12">
<button class="btn btn-primary" th:onclick="'window.location.pathname=\'' + @{/} + '\''">Volver</button>
</span>
<span>2018</span>
</footer>
</body>
</html>

View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Error</title>
<link rel="stylesheet" th:href="@{/webjars/bootstrap/3.3.7-1/css/bootstrap.min.css}">
<script th:src="@{/webjars/jquery/3.1.1-1/jquery.min.js}"></script>
<script th:src="@{/webjars/bootstrap/3.3.7-1/js/bootstrap.min.js}"></script>
</head>
<body class="text-center text-danger">
<h2>Error</h2>
<h4>El archivo ya existe o hubo un error</h4>
<button class="btn btn-primary" th:onclick="'window.location.pathname=\'' + @{/} + '\''">Volver</button>
</body>
</html>

View File

@ -2,57 +2,83 @@
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Arjion</title> <title>Arjion</title>
<link rel="stylesheet" th:href="@{/webjars/bootstrap/3.3.7-1/css/bootstrap.min.css}"> <link rel="stylesheet" th:href="@{/webjars/bootstrap/3.3.7-1/css/bootstrap.min.css}">
<link rel="stylesheet" th:href="@{/css/main.css}"> <link rel="stylesheet" th:href="@{/css/main.css}">
<script th:src="@{/webjars/jquery/3.1.1-1/jquery.min.js}"></script> <script th:src="@{/webjars/jquery/3.1.1-1/jquery.min.js}"></script>
<script th:src="@{/webjars/bootstrap/3.3.7-1/js/bootstrap.min.js}"></script> <script th:src="@{/webjars/bootstrap/3.3.7-1/js/bootstrap.min.js}"></script>
<script th:src="@{/js/main.js}"></script> <script th:src="@{/js/main.js}"></script>
</head> </head>
<body> <body>
<header class="text-center"> <header class="text-center">
<a th:href="@{/}"><h1 class="text-primary">Arjion</h1></a> <a th:href="@{/}">
</header> <h1 class="text-primary">Arjion</h1>
<section class="text-center col-md-4 col-md-offset-4"> </a>
<form class="form-horizontal form-label-left" method="post" enctype="multipart/form-data" novalidate="novalidate" th:action="@{/upload}" <h3 class="text-warning">[[${documentoVO.count}]] archivos</h3>
th:object="${documentForm}"> </header>
<div class="input-group"> <section class="text-center col-md-4 col-md-offset-4">
<span class="input-group-addon"> <form class="form-horizontal form-label-left" method="post" enctype="multipart/form-data" novalidate="novalidate" th:action="@{/upload}"
<span class="glyphicon glyphicon-file"></span> th:object="${documentForm}">
</span> <div class="input-group">
<label class="input-group-addon custom-file"> <span class="input-group-addon">
<input type="file" name="archivos" class="custom-file-input" multiple="multiple" /> <span class="glyphicon glyphicon-file"></span>
<span class="custom-file-control"></span> </span>
</label> <label class="input-group-addon custom-file">
<span class="input-group-addon"> <input type="file" name="archivos" class="custom-file-input" multiple="multiple" />
<button class="btn btn-primary" type="submit"> <span class="custom-file-control"></span>
<span class="glyphicon glyphicon-level-up"></span> </label>
</button> <span class="input-group-addon">
</span> <button class="btn btn-primary" type="submit">
</div> <span class="glyphicon glyphicon-level-up"></span>
</form> </button>
</section> </span>
<hr class="col-md-12"> </div>
<section> </form>
<div th:each="doc : ${documentoVO.archivos}" class="col-md-12"> </section>
<span class="col-md-1 text-primary lead">Nombre</span> <section class="col-md-12">
<span class="col-md-11 text-primary lead">[[${doc.nombre}]]</span> <hr>
<span class="col-md-1 text-muted">Tamaño</span> </section>
<span class="col-md-11 text-muted">[[${doc.tamano}]] bytes</span> <section class="col-md-12">
<span class="col-md-1 text-muted">Lenguaje</span> <div th:each="arc : ${documentoVO.archivos}" class="col-md-12">
<span class="col-md-11 text-muted">[[${doc.lenguaje}]]</span> <span class="col-md-1 text-primary lead">Nombre</span>
<span class="col-md-1 text-success">Metadatos</span> <span class="col-md-11 text-primary lead">[[${arc.nombre}]]</span>
<span class="col-md-11 text-success">[[${doc.metadata}]]</span> <span class="col-md-1 text-muted">Tamaño</span>
<span class="col-md-1 text-warning">Contenido</span> <span class="col-md-11 text-muted">[[${arc.tamano}]] bytes</span>
<pre class="col-md-11 text-warning">[[${doc.contenido}]]</pre> <span class="col-md-1 text-muted">Lenguaje</span>
<hr class="col-md-12"> <span class="col-md-11 text-muted">[[${arc.lenguaje}]]</span>
</div> <span class="col-md-1 text-success">Metadatos</span>
</section> <span class="col-md-11 text-success">[[${arc.metadata}]]</span>
<footer class="col-md-12 text-center"> <span class="col-md-1 text-warning">Contenido</span>
<span>2018</span> <pre class="col-md-11 text-warning">[[${arc.contenido}]]</pre>
</footer> <span class="col-md-12">
<hr>
</span>
</div>
</section>
<section class="col-md-12">
<hr>
</section>
<section class="col-md-12 text-center">
<div th:each="doc : ${documentoVO.documentos}">
<div>
<span class="col-md-12 text-primary">
<a th:href="@{'/detail'(nombre=${doc.nombre})}" class="lead">[[${doc.nombre}]]</a> -
<a th:href="@{'/download'(filename=${doc.nombre})}">download</a>
<br>[[${doc.tamano}]] bytes
<br>[[${doc.lenguaje}]]</span>
<span class="col-md-12 text-success">[[${#strings.abbreviate(doc.metadata,200)}]]</span>
<span class="col-md-12 text-warning">[[${#strings.abbreviate(doc.contenido,200)}]]</span>
</div>
<span class="col-md-12">
<hr>
</span>
</div>
</section>
<footer class="col-md-12 text-center">
<span>2018</span>
</footer>
</body> </body>
</html> </html>