miércoles, 7 de agosto de 2019

FILE UPLOAD IN DATABASE JAVA SPRING JDBC

En el proyecto maven de java con spring MVC, tenemos que crear un controller para el fileupload de forma async mediante ajax.

@Controller
@RequestMapping("/upload")
public class FileUploadController {

   @Autowired
   DocumentoAdjuntoService documentoAdjuntoService;
   @Autowired
    ParametroValorService parametroValorService;
 
   @PostMapping("/changefile")
   public @ResponseBody Message fileUpload(MultipartHttpServletRequest request)
         throws IOException {
       String url = System.getProperty("user.home");
           
       Util _util = new Util();
        List<ParametroValor> lstResult = parametroValorService.list(Constantes.Parametros.URL);
        for (ParametroValor oItem : lstResult) {
            url += File.separator + oItem.getValor() +File.separator;
        }
       Message msj = _util.sendFileServer(request,url);
       return msj;
   }
 
   @RequestMapping(value = "/savefile", method = RequestMethod.POST)
   public @ResponseBody Message sendFileUpload(@RequestBody DocumentoAdjunto item)
   {
       Message  msj = documentoAdjuntoService.set(item);
       return msj;
   }
 
}

En esta clase observamos que existen dos funciones, el primero fileUpload que es invocado cuando se carga por primera vez el documento desde el cliente y la otra función sendFileUpload cuando se el cliente confirma guardar el documento.
La linea de codigo System.getProperty("user.home") obtiene una ruta base del servidor de aplicaciones donde se va almacenar el documento de forma temporal. La función sendFileServer se detalla a continuación:

public Message sendFileServer(MultipartHttpServletRequest request, String urlServidor) throws IOException{
        Message msj = new Message();
        File f = new File(urlServidor);
        if(!f.exists()){
           f.mkdirs();
        }
        DocumentoAdjunto obj = new DocumentoAdjunto();
        Iterator<String> itr = request.getFileNames();
       MultipartFile mpf = null;
        while(itr.hasNext()){
                mpf = request.getFile(itr.next());
                String nameFile = new SimpleDateFormat("ddMMyyHHmmss").format(new Date());
                nameFile = nameFile + mpf.getOriginalFilename().replace(" ", "-");
                obj.setURLDOC(urlServidor+ "-"+nameFile);
            try{
                FileCopyUtils.copy(mpf.getBytes(), new FileOutputStream(urlServidor+ nameFile));
                obj.setNOMDOC(nameFile);                
                msj.setSuccess(true);
                msj.setData(obj);
                msj.convert(Constantes.Mensajes.MensajeFileUploadExito);
                
            }
            catch(Exception ex){
                msj.setSuccess(false);
                msj.setData(obj);
                msj.setException(ex.getMessage());
                msj.setType(Constantes.Mensajes.typeError);
                msj.convert(Constantes.Mensajes.MensajeErrorExcepcion);
            }
        }
        return msj;
    }

Dicha función copia el documento del cliente al la ruta base que recibe como parámetro "urlServidor", asimismo se almacena con un nombre generado (parámetro nameFile) con el formato de ddMMyyHHmmss y nombre del archivo.

En la clase tambien observamos la funcion sendFileUpload, evento que se activa cuando el cliente decide guardar el documento en la base de datos. Por lo tanto, en el repositorio DocumentoAdjuntoDataImplement.java debe tener la siguiente estructura:

@Repository("documentoAdjuntoData")
public class DocumentoAdjuntoDataImplement implements DocumentoAdjuntoData {
    
    private SimpleJdbcCall insertarDocumentoAdjuntoBrechaIndicador;
    
    @Autowired
    public void setDataSource(DataSource dataSource) {

        this.insertarDocumentoAdjuntoBrechaIndicador = new SimpleJdbcCall(dataSource)
                .withSchemaName(Constantes.BDContext.Schema)
                .withCatalogName(Constantes.BDContext.PKG_ADMINISTRACION)
                .withProcedureName("USP_INS_DOCUMENTADJUNTO");
       
    }

   @Override
    public Integer set(DocumentoAdjunto item) {
          String oCodigo = "";
        SqlParameterSource in = new MapSqlParameterSource()
        .addValue("V_URLDOC", item.getURLDOC())
        .addValue("V_NOMDOC", item.getNOMDOC())
        .addValue("B_BYTEDOC",  new SqlLobValue(new ByteArrayInputStream(item.getByteDOC()), 
        item.getByteDOC().length, new DefaultLobHandler()), Types.BLOB)
        .addValue("V_USUCREACIO", item.getUsucreacio());

        Map map = insertarDocumentoAdjuntoBrechaIndicador.execute(in);
        List<Map> result = (List<Map>) map.get("CV");
        for (Map obj : result) {
            oCodigo = Format.toString(obj.get("MSG"));
        }

        return Integer.parseInt(oCodigo);
    }


}

GENERATE FILE FROM ARRAY BYTE USING JAVA SPRING JDBC

En el proyecto de java ubicamos nuestra clase DAOImplement para obtener los datos del documento a generar a través de un array de byte.

DocumentoAdjuntoDataImplement.java

@Repository("documentoAdjuntoData")
public class DocumentoAdjuntoDataImplement implements DocumentoAdjuntoData {

private SimpleJdbcCall getDocumentoAdjunto;

 @Autowired
public void setDataSource(DataSource dataSource) {

       
        this.getDocumentoAdjunto = new SimpleJdbcCall(dataSource)
                .withSchemaName(Constantes.BDContext.Schema)
                .withCatalogName(Constantes.BDContext.PKG_ADMINISTRACION)
                .withProcedureName("USP_GET_DOCUMENADJUNTO")
                .returningResultSet("CV", new DocumentoAdjuntoRowMapper());
       
    }

@Override
    public DocumentoAdjunto get(DocumentoAdjunto item) {
        DocumentoAdjunto doc = new DocumentoAdjunto();
        try {
            SqlParameterSource in = new MapSqlParameterSource()
                    .addValue("V_NOMDOC", item.getNOMDOC());
           Map<String, Object> out = getDocumentoAdjunto.execute(in);
           List<DocumentoAdjunto> result = (List<DocumentoAdjunto>) out.get("CV");
           return result.get(0);

        } catch (Exception e) {
            System.out.println(e);
        }
        return doc;
    }

private static class DocumentoAdjuntoRowMapper implements RowMapper<DocumentoAdjunto> {
        @Override
        public DocumentoAdjunto mapRow(ResultSet resultSet, int rowNum) throws SQLException {
            DocumentoAdjunto doc = new DocumentoAdjunto();
            doc.setIDDOCUMENT(resultSet.getInt(("IDDOCUMENT")));
            doc.setNOMDOC(resultSet.getString("NOMDOC"));
            doc.setDESCDOC(resultSet.getString("DESCDOC"));
            doc.setURLDOC(resultSet.getString("URLDOC"));
            doc.setFeccreacio(resultSet.getString("FECCREACIO"));
            doc.setUsucreacio(resultSet.getString("USUCREACIO"));
            doc.setDESCPROCACTUAL(resultSet.getString("VALOR"));
            doc.setPROCACTUAL(resultSet.getInt("PROCACTUAL"));

            Blob blob = resultSet.getBlob("ARCHIVO");
            byte[] bytes = blob.getBytes(1, (int) blob.length());
            doc.setByteDOC(bytes);
            return doc;
        }
    }



En esta clase observamos que en el constructor setDataSource instancia la conexión a la base de datos declarando al StoredProcedure con su respectivo esquema y paquete, asimismo se declara un resultset que invocará a la función DocumentoAdjuntoRowMapper que se va disparar cuando se obtenga los resultado del execute. Es importante resaltar que dentro de la clase RowMapper se hace la conversión del BLOB a un array de byte[], para la creación del archivo mas adelante.


Luego, tenemos una clase controller llamado FileDownloadController.java  que tiene una función de tipo POST que realiza la descarga mediante la url .../file/{nombreArchivo} tal como se declara en el @RequestMapping.

@Controller
@RequestMapping("/download")
public class FileDownloadController {

    @Autowired
    ParametroValorService parametroValorService;
    @Autowired
    DocumentoAdjuntoService  _DocumentoAdjuntoService;

@RequestMapping("/file/{fileName:.+}")
    public void downloadPDFResource(HttpServletRequest request,
            HttpServletResponse response,
            @PathVariable("fileName") String fileName) {
       
        List<ParametroValor> lstResult = parametroValorService.list(Constantes.Parametros.URL);
        for (ParametroValor oItem : lstResult) {
            
            //OBTENEMOS EL ARREGLO DE BYTE COMO ATRIBUTO DEL OBJETO DocumentoAdjunto
            DocumentoAdjunto item = new DocumentoAdjunto();
            item.setNOMDOC(fileName);
            item = _DocumentoAdjuntoService.getDocumento(item);
            
            //GENERAMOS EL DOCUMENTO DESDE ARRAY BYTE
            String urlWriteFile = System.getProperty("user.home");
            urlWriteFile += File.separator + oItem.getValor() +File.separator + fileName;
            Util.writeBytesToFile(item.getByteDOC(), urlWriteFile);
            Path pathfileOrigen = Paths.get(urlWriteFile);
            
            //DESCARGAMOS EL ARCHIVO SEGUN LA EXTENSION    
            File newfile = new File(urlWriteFile);
            String extension = Util.getFileExtension(newfile);
            switch (extension) {
                case "doc":
                    response.setContentType("application/msword");
                    break;
                case "docx":
                    response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
                    break;
                default:
                    response.setContentType("application/"+extension+"");
                    break;
            }
            response.setHeader("Content-Disposition","attachment;filename="+newfile.getName());
         
            try {
                Files.copy(pathfileOrigen, response.getOutputStream());
                response.getOutputStream().flush();
                Util.deleteFile(urlWriteFile);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
           
        }

    }
}


En esta clase observamos los siguientes parámetros:
lstResult: Obtener un parámetro de la base de datos para utilizar como nombre del archivo donde pondremos documentos de forma temporal. 
item: Objeto que filtra los datos del documento mediante el nombre del archivo y a la vez devuelve el arreglo de bytes desde la base de datos.
urlWriteFile: Se genera una ruta especifica donde se van alojar los archivos, ya sea en windows o linux, al fin a cabo será de forma temporal. Por ejemplo para linux genera una ruta "/home/oracle/[lstResult]/[item.filename]".
writeBytesToFile: La función que genera el documento:
public static void writeBytesToFile(byte[] bFile, String fileDest) {
        try (FileOutputStream fileOuputStream = new FileOutputStream(fileDest)) {
            fileOuputStream.write(bFile);
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
response.setContentType: Tipo de extensión del archivo a descargar.
deleteFile: Función que elimina el archivo una vez descargado:
public static void deleteFile(String urlFile) throws IOException{
        
        try
        { 
            Files.deleteIfExists(Paths.get(urlFile)); 
        } 
        catch(NoSuchFileException e) 
        { 
            System.out.println("No such file/directory exists"); 
        } 
        catch(DirectoryNotEmptyException e) 
        { 
            System.out.println("Directory is not empty."); 
        } 
        catch(IOException e) 
        { 
            System.out.println("Invalid permissions."); 
        } 

    }
getFileExtension: Función que devuelve la extensión de un documento:
public static String getFileExtension(File file) {
        String fileName = file.getName();
        if(fileName.lastIndexOf(".") != -1 && fileName.lastIndexOf(".") != 0)
        return fileName.substring(fileName.lastIndexOf(".")+1);
        else return "";

    }