unit u_uploadarchi;

{$mode delphi}

interface

uses
  Classes, SysUtils,
  uauxiliares,
  uDataSetGenerico, urosx, uConstantesSimSEE;

const
  MAX_FILE_BLOCK = 1024*100;
// si la cantidad de bytes a almacenar supera MAX_FILE_BLOCK los datos
// se parten en bloques de MAX_FILE_BLOCK y se almacenan en una tabla de blocks
// aparte. Esto es así para evitar porblemas de transimisión de datos sobre http


// sube el archivo a la carpeta identificada por nid_carpeta.
// si no detecta error retorna el nid del archivo asignado en la DB.
// si hay error retorna -1.
procedure upload_archi(dbcon: TDBrosxCon; tbl_archivo_bloques: string; var nid,
  nid_version: integer; out nidarchivo: integer; zipname,archi,data,permiso: string; PASAPORTE: string);

// Crea la carpeta "path" en la tabla tbl_carpeta
// devuelve nid_Carpeta o -1 en error
//function crear_carpeta (dbcon: TDBrosxCon; tbl_carpeta, tbl_archivos, path: string; tipo: integer;
    //                   descripcion: string; vaciarSiExiste:Boolean=false):Integer;

// Elimina todos los archivos (y sus bloques) asociados a la carpeta
// nid_carpeta
//procedure clear_carpeta(dbcon: TDBrosxCon; tbl_archivo: string; nid_carpeta: integer);


// Baja los archivo asociados a nidarchivo y los almacena en la carpeta
// destino. Si no se pasa CarpetaDestino, o se pasa un string vacio se
// utiliza como carpeta destino el directorio de SimSEE local
// Si el parámetro xflg se opmite o es verdadero, y si el nombre de archivo
// contiene el string: ".zip" se descomprime en una sub-carpeta
// con igual nombre de archivo y luego se borra el arhivo. Si xunzip= false
// no se intenta descomprimir el archivo.
function bajarCarpeta(dbcon: TDBrosxCon;  tbl_archivo: string; nidarchivo,
                       nid_version: integer; PASAPORTE,archi: string; carpetaDestino:
                       string = ''; xunzip: boolean = True): boolean;

// en caso de error de alguna de las funciones anteriores, llamando
// esta función se puede obtener información sobre el error ocurrido.
function UltimoError: string;

implementation

var
  error: string;

function UltimoError: string;
begin
  Result := error;
end;



function bajarCarpeta(dbcon: TDBrosxCon; tbl_archivo: string; nidarchivo,
  nid_version: integer; PASAPORTE, archi: string; carpetaDestino: string;
  xunzip: boolean): boolean;
var
  pathCarpeta, nomArch: string;
  chkSumRemoto, chkSumLocal: ShortString;
  orden: string;
  f: TextFile;
  ds, ds_Bloques: TResultadoQuery;
  rArchi, rBloque: TDataRecord;
  s, datos, archivo,Nombre_zip: string;

  nbloques, kbloque: integer;

begin
  Result := False;


  ds := dbcon.sql_query_pass(PASAPORTE,nidarchivo,nid_version);


  if ds.EOF then
  begin
    Result := False;
    exit;
  end;

  if ds.nid=-2 then
  begin
    Result:=false;
    exit;
  end;

  rArchi := ds.First;

  if carpetaDestino = '' then
    pathCarpeta := getDir_SimSEE
  else
    pathCarpeta := carpetaDestino;

  ForceDirectories(pathCarpeta);


  while not ds.EOF do
  begin
    nbloques := rArchi.GetByNameAsInt('nbloques');

    orden := 'SELECT nid, checksum, nombre_archi,kbloque FROM ' + tbl_archivo +
      ' WHERE nid_archivo = ' + IntToStr(nidArchivo) + ' ORDER BY kbloque LIMIT '+
      IntToStr(nbloques);
    ds_Bloques := dbcon.query(orden);
    rBloque := ds_Bloques.First;

    nomArch := rBloque.GetByNameAsString('nombre_archi');
    Nombre_zip:=  ChangeFileExt(nomArch,'.zip');
    // baja archivo multibloque
    AssignFile(f, pathCarpeta + DirectorySeparator + Nombre_zip);
    Rewrite(f);
    kBloque:=0;
    while not ds_Bloques.EOF do
    begin
      inc(kBloque);
      if kBloque <>  rBloque.GetByNameAsInt('kbloque') then
      begin
        error := 'Falta un bloque en la nube';
        Result := False;
        exit;
      end;
      WriteLn('Bajando bloque: ' + IntToStr(kBloque)+' de ' + IntToStr(nbloques));
      chkSumRemoto := rBloque.GetByNameAsString('checksum');
      s:= dbcon.sql_func('SELECT datos FROM '
            + tbl_archivo+' WHERE nid = '
            + rBloque.GetByNameAsString('nid'));

      if dbcon.tipoServidor = CTS_MySQL then
         datos := HexStrToBuffStr_( s )
      else
        datos := PG_HexStrToBuffStr( s );

      chkSumLocal := checksum(@datos[1], length(datos));
      if chkSumLocal = chkSumRemoto then
      begin
        Write(f, datos);
      end
      else
      begin
        error := error + nomArch + #13#10;
      end;
      rBloque := ds_bloques.Next;
    end;
    CloseFile(f);
    if xunzip then
    begin
      Unzip(pathCarpeta + Nombre_zip);
      deletefile(pathCarpeta +  Nombre_zip);
      archivo:=pathCarpeta +  nomArch; //nombre original, viene de la BD
      RenameFile(archivo, archi );
      Result:=true
    end;
    ds_Bloques.Free;

    rArchi := ds.Next;
  end;
  ds.Free;

end;
{
function crear_carpeta(dbcon: TDBrosxCon; tbl_carpeta, tbl_archivos, path: string; tipo: integer;
  descripcion: string; vaciarSiExiste: Boolean): Integer;
var
  nid_carpeta: integer;
  cnt: integer;
begin
  Result := -1;
  cnt := StrToInt( dbcon.sql_func(
    'SELECT count(*) FROM '+tbl_carpeta+' WHERE path=''' + path + ''''));
  if cnt = 0 then
  begin
    nid_carpeta:= dbcon.sql_nextnid(tbl_carpeta);
    if dbcon.sql_exec('INSERT INTO '+ tbl_carpeta+' ( nid, fecha, tipo, path, descripcion ) VALUES ('
       + IntToStr(nid_carpeta) + ', now(), ' +IntToStr(
       tipo) + ', ''' + path + ''', ''' + Descripcion + ''' ) ') then

       Result := nid_carpeta;
  end
  else
  begin
    nid_carpeta:= StrToInt (dbcon.sql_func('SELECT nid FROM '+tbl_carpeta+' WHERE path=''' + path + ''' LIMIT 1'));
    if vaciarSiExiste then
      clear_carpeta(dbcon, tbl_archivos, nid_carpeta);

  end;
  Result := nid_carpeta;


end;   }
{
procedure clear_carpeta(dbcon: TDBrosxCon; tbl_archivo: string; nid_carpeta: integer);
var
  sql: string;
begin
  if dbcon.tipoServidor = CTS_MySQL then
  sql:= 'DELETE FROM ' + tbl_archivo + '_bloques USING ' + tbl_archivo
   +' LEFT JOIN '+ tbl_archivo+'_bloques ON '
   + tbl_archivo + '.nid = ' + tbl_archivo + '_bloques.nid_archivo '
   +' WHERE ' + tbl_archivo + '.nid_carpeta = ' + IntToStr(nid_carpeta)
  else
  sql:= 'DELETE FROM ' + tbl_archivo + '_bloques USING ' + tbl_archivo
   +' WHERE '
   + tbl_archivo + '.nid = ' + tbl_archivo + '_bloques.nid_archivo '
   +' AND ' + tbl_archivo + '.nid_carpeta = ' + IntToStr(nid_carpeta);

  dbcon.sql_exec( sql );

  dbcon.sql_exec('DELETE FROM ' + tbl_archivo + ' WHERE nid_carpeta = ' +
    IntToStr(nid_carpeta));
end;      }

procedure upload_archi( dbcon: TDBrosxCon; tbl_archivo_bloques: string;
  var nid, nid_version: integer; out nidarchivo: integer;
  zipname, archi, data, permiso: string;
  PASAPORTE: string);
var
  sql: string;
  nombreArchivo: string;
  arch: string;
  chksum: ShortString;
  fb: file of byte;
  nbytes: longint;
  nidBloque: integer;
  k_bloque: integer;
  bytes_restantes: integer;
  flg_error: boolean;
  nBytesTotal: integer;
begin
  error := '';

  nombreArchivo := ExtractFileName(archi);

  assignfile(fb, zipname);
  reset(fb);
  nbytes := system.filesize(fb);
  nBytesTotal:= nbytes;

  begin
    k_bloque := 0;
    bytes_restantes := nbytes;
    flg_error := False;
    nidarchivo:=dbcon.sql_exec_pass(PASAPORTE,data,nid_version,nid,
                               k_bloque,permiso); //el kbloque se sube despues que se sepa la cantidad de bloques subidos
    while (bytes_restantes > 0) and not flg_error do
    begin
      Inc(k_bloque);

      if bytes_restantes > MAX_FILE_BLOCK then
        nbytes := MAX_FILE_BLOCK
      else
        nbytes := bytes_restantes;

      setlength(arch, nbytes);
      blockread(fb, arch[1], nbytes);
      chksum := checksum(@arch[1], Length(arch));
      nidBloque := dbcon.sql_nextnid(tbl_archivo_bloques);

      sql := 'INSERT INTO ' + tbl_archivo_bloques +
        ' (nid, nid_archivo, kbloque, datos, checksum, nombre_archi) ' +
        ' VALUES (' + IntToStr(nidBloque) + ', ' + IntToStr(nidArchivo) +
        ', ' + IntToStr(k_bloque) + ', ''' + PG_BuffStrToHexStr( arch ) +
        ''', ''' + chksum + ''', '''+ nombreArchivo + ''')';

      if dbcon.sql_exec(sql) then
      begin
        bytes_restantes := bytes_restantes - nbytes;
        writeln( 100*(1 - bytes_restantes / nBytesTotal ) );
      end
      else
      begin
        error := sql + #13#10 + dbcon.UltimoError;
        flg_error := True;
      end;
    end;
    closefile(fb);
    if flg_error then
    begin
      sql := 'DELETE FROM ' + tbl_archivo_bloques + ' WHERE nid_archivo= ' +
        IntToStr(nidArchivo);  //si no se pudieron carga todos los bloques, los eliminamos
      dbcon.sql_exec(sql);
      nid := -1;
    end
    else
    begin
      sql := 'UPDATE  nube_simsee SET  nbloques  =' + IntToStr(k_bloque) +
                      'WHERE nid_archivo = '+ IntToStr(nidArchivo) ;
      dbcon.sql_exec(sql);
      if nid=-2 then
        raise Exception.Create( 'u_uploadarchi.upload_archi , Error: ' +dbcon.ultimoError);
    end;
  end;
end;

begin
  error := '';

end.

