IdFTP - nadpisanie istniejącego pliku nowym

0

W aplikacji próbuję wysyłać pliki tekstowe na serwer FTP za pomocą IdFTP. Wszystko idzie gładko póki plik o danej nazwie nie znajduje się na serwerze. Dziwi mnie jednak fakt, że wysłanie pliku za pomocą:

IdFTP.Put('plik.txt', 'plik.txt', True);

zamiast wywołać błąd, że plik o takiej nazwie znajduje się już na serwerze lub nadpisać go w stylu - zastąp poprzedni plik nowym - to jakimś cudem treść z pliku który ma być przeniesiony jest zapisywana wewnątrz pliku który już znajduje się na serwerze.

Orientuje się ktoś z was może jak ugryźć ten temat?

Po prostu chciałbym by plik który już się znajduje na serwerze o takiej samej nazwie jak przenoszony był najpierw usuwany po czym wgrałby się ten nowy, docelowy plik.

Wiem, że mogę użyć IdFTP.Delete do usunięcia pliku, jednak jak sprawdzić czy w ogóle plik o takiej nazwie już jest na serwerze?

0

Wiem, że mogę użyć IdFTP.Delete do usunięcia pliku, jednak jak sprawdzić czy w ogóle plik o takiej nazwie już jest na serwerze?

@Zeelof - zobacz tutaj http://forums.devshed.com/delphi-programming-90/idftp-client-checking-files-exist-302504.html

2
Zeelof napisał(a):

Dziwi mnie jednak fakt, że wysłanie pliku za pomocą:

IdFTP.Put('plik.txt', 'plik.txt', True);

zamiast wywołać błąd, że plik o takiej nazwie znajduje się już na serwerze lub nadpisać go w stylu - zastąp poprzedni plik nowym - to jakimś cudem treść z pliku który ma być przeniesiony jest zapisywana wewnątrz pliku który już znajduje się na serwerze.

A wiesz w ogóle co piszesz czy kopiuj wklej z innych stron? Mam wrażenie że to drugie.
Trzecim parametrem metody Put jest Append` z domyślną wartością False Jeżeli ustawisz Append na True (jak to zrobiłeś) oznacza że chcesz zapisywać w istniejącym pliku (nie podałeś czwartego parametru który oznacza pozycję domyślnie -1 czyli na końcu) więc Indy robi dokładnie to co mu kazałeś. Tak właśnie róbcie a później kolejny narzeka na Indy że jest be, że nie działa itd.
Zwyczajnie:

IdFTP.Put('plik.txt', 'plik.txt');

I będzie śmigało tak jak chcesz. Nie musisz sprawdzać czy plik istnieje . Jeżeli istnieje to go zastąpi.

0

Ok super tylko mimo wszystko potrzebuje sprawdzenia czy plik już istnieje, gdyż dalsza część kodu będzie sprawdzać czy dany plik jest już na serwerze i jeśli nie to będzie tworzyć linię tekstu i pod nią dopisywać nowe rekordy wynikające z działania aplikacji, zaś jeśli plik będzie już istniał to ma do niego tylko dopisać rekordy wynikające z działania aplikacji.

0

Jeśli dobrze pamiętam, to w taki sposób możesz sprawdzić czy plik istnieje na serwerze:

if not (ftp.size(nazwa_pliku <= 0)) then ...

Albo bez NOT, no ale tego tłumaczyć już chyba nie muszę.

0

Ok super tylko mimo wszystko potrzebuje sprawdzenia czy plik już istnieje, [...]

@Zeelof - przecież podałem Ci link z przykładem w tym poście (ten sam link i inne dwa podał @Paweł Dmitruk).

1

Po pierwsze to jest napisane błędnie, po drugie sprawdzenie czy plik istnieje po rozmiarze to śmiech na sali a co jeżeli plik będzie tyle że z rozmiarem 0 bajtów?
Tu masz przykład poprawnego rozwiązania:

function FTPFileExists(IdFtp: TIDFtp; FileName: string): Boolean;
var
  i: Integer;
begin
  result:= False;
  IdFtp.List(FileName, True);  //pobieramy liste pasujacych elementow
  for i:= 0 to IdFTP.DirectoryListing.Count - 1 do
  begin
    if  IdFTP.DirectoryListing.Items[i].ItemType = ditFile then //czy to plik a nie np. folder lub dowiazanie
    begin
      result:= True;
      break;
    end;
  end;
end;

przykład użycia:

        if FTPFileExists(IdFTP, 'test.txt') then
          ShowMessage('plik istnieje')
        else
          ShowMessage('nie ma takiego pliku');
0

Niestety nie mogę skompilować samej funkcji ;/

[Error] Unit1.pas(430): Incompatible types: 'TStrings' and 'String'
[Error] Unit1.pas(433): Undeclared identifier: 'ditFile'
[Fatal Error] Project2.dpr(8): Could not compile used unit 'Unit1.pas'
0

Pierwszy błąd nie wiem, trzeba by wiedzieć jaką masz wersje Indy być może w innej wersji jest trochę inaczej.
Drugi błąd dodaj do uses IdFTPList

EDIT:
@Zeelof tak w ciemno sprawdź:

function FTPFileExists(IdFtp: TIDFtp; FileName: string): Boolean;
var
  i: Integer;
  sl: TStringList;
begin
  result:= False;
  sl:= TStringList.Create;
  try
    IdFtp.List(sl, FileName, True);  //pobieramy liste pasujacych elementow
    for i:= 0 to sl.Count do
    begin
      if Pos ('type=file', sl.Strings[i]) > 0 then //czy plik
       begin
         result:= True;
         break;
      end;
    end;
  finally
    sl.Free;
  end;
end;
0

Na pewno się skompilowało jednak dalej jest problem ze sprawdzeniem pliku.
U mnie jest to tak:

     with IdFTP do
      begin
        Password := 'xxx';
        Username := 'xxx';
        Host := 'xxx';
        Form1.Enabled := False;
        Connect(True, -1);
        if FTPFileExists(IdFTP, 'a.csv') then
          ShowMessage('plik istnieje')
        else
          ShowMessage('nie ma takiego pliku');
        Disconnect;
        Form1.Enabled := True;
      end;

Po wykonaniu nie wyrzuca żadnego ShowMessage...

0

Niemożliwe a poza tym od czego debugger, załóż breakpointa na początku funkcji i wykonaj kod linia po linii.

0

Faktycznie problem leżał po mojej stronie - miałem prędzej nieco kodu sprawdzającego połączenie który był ustawiony na niełączenie z serwerem.
Niestety funkcja też nie chce przepuścić apki ;/
Po wyłonieniu aplikacja po prostu się zawiesza i nic nie idzie zrobić prócz wymuszonego zamknięcia ;/
Debugger zaś jest mi obcy przez co nie mam jak go użyć ;/

0

A w ogóle jesteś pewien że zawiesza się na tej funkcji a nie na. na łączeniu z serwerem?

0

Tak na 100%.
Dałem dwa buttony, jeden:

with IdFTP do
      begin
        Password := 'xxx';
        Username := 'xxx';
        Host := 'xxx';
        Form1.Enabled := False;
        Connect(True, -1);
        if FTPFileExists(IdFTP, 'a.csv') then
          ShowMessage('plik istnieje')
        else
          ShowMessage('nie ma takiego pliku');
        Disconnect;
        Form1.Enabled := True;
      end;

i drugi:

with IdFTP do
      begin
        Password := 'xxx';
        Username := 'xxx';
        Host := 'xxx';
        Form1.Enabled := False;
        Connect(True, -1);
        IdFTP.Put('files\a.csv', 'a.csv');
        Disconnect;
        Form1.Enabled := True;
      end;

I w tym drugim przypadku bez problemu przesyła plik na serwer.

0

A co to za wersja Indy może jakiś zabytek 9.x z przed 15 lat i chcesz aby działało. Kliknij prawym na IdFTP i wybierz About... tam powinna być wersja. A przy okazji jaka wersja Delphi?

0

Wersja Indy 9.00.10
Delphi 7 Enterprise

1

Koniecznie zmień ten zabytek na 10. Instrukcja zobacz mój post Zainstalowanie Indy 10 na Delphi 7 PE
Oczywiście wcześniej odinstaluj starą 9 w Component -> Install Packages (tam chyba będą 2 pozycje związane z Indy zaznaczaj i klikaj Remove)

0

Na nowych komponentach Indy problem wygląda następująco:
a.csv: No such file or directory
I przenosi do kodu komponentu (IdReplyRFC.pas):

procedure TIdReplyRFC.RaiseReplyError;
begin
  raise EIdReplyRFCError.CreateError(NumericCode, Text.Text);
end;

Samo połączenie i wysyłanie plików działa poprawnie więc komponenty są w porządku, zaś komunikat nawet w przypadku braku pliku powinien raczej być ten który się ustaliło w procedurze, prawda?

0

Ale w ogóle którą wersję robisz na nowym to chyba to pierwsze które podałem?

0

Próbowałem na obu i jest to samo.

0

Sprawdziłem mój pierwszy przykład na tej wersji Indy i działa prawidłowo. Pokaż kod bo błąd jest pewnie gdzie indziej.

0

Może jednak nadal te komponenty coś nie do końca działają -.-' Tylko dlaczego połączenie i wysyłanie działa poprawnie na tym samym komponencie? ;/

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
  IdExplicitTLSClientServerBase, IdFTP, StdCtrls, IdFTPList;

type
  TForm1 = class(TForm)
    Button1: TButton;
    IdFTP: TIdFTP;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function FTPFileExists(IdFtp: TIDFtp; FileName: string): Boolean;
var
  i: Integer;
begin
  result:= False;
  IdFtp.List(FileName, True);  //pobieramy liste pasujacych elementow
  for i:= 0 to IdFTP.DirectoryListing.Count - 1 do
  begin
    if  IdFTP.DirectoryListing.Items[i].ItemType = ditFile then //czy to plik a nie np. folder lub dowiazanie
    begin
      result:= True;
      break;
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  with IdFTP do
  begin
    Password := 'xxx';
    Username := 'xxx';
    Host := 'xxx';
    Form1.Enabled := False;
    Connect;
    if FTPFileExists(IdFTP, 'a.csv') then
      ShowMessage('plik istnieje')
    else
      ShowMessage('plik nie istnieje');
    Disconnect;
    Form1.Enabled := True;
  end;
end;

end.

EDIT: Komponenty jednak działają poprawnie, na RAD Studio z wbudowanymi komponentami Indy w wersji 10.6.2.5298 po kompilacji występuje ten sam błąd.

0

Przecież ten kod działa normalnie na dokładnie tej samej wersji Indy i Delphi nie ma żadnego błędu o którym piszesz. Nie mam pojęcia co zrobiłeś :/ Project -> Build... i wtedy sprawdź.

@Zeelof
Może to wina konfiguracji serwera sprawdź przykładowo na tym

function FTPFileExists(IdFtp: TIDFtp; FileName: string): Boolean;
var
  i: Integer;
begin
  result:= False;
  IdFtp.List(FileName, True);  //pobieramy liste pasujacych elementow
  for i:= 0 to IdFTP.DirectoryListing.Count - 1 do
  begin
    if  IdFTP.DirectoryListing.Items[i].ItemType = ditFile then //czy to plik a nie np. folder lub dowiazanie
    begin
      result:= True;
      break;
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 with IdFTP do
  begin
    Passive:= True;
    Password := '';
    Username := 'anonymous';
    Host := 'ftp.rarlab.com';
    Form1.Enabled := False;
    Connect;
    if FTPFileExists(IdFTP, 'a.csv') then
      ShowMessage('plik istnieje')
    else
      ShowMessage('plik nie istnieje');
    Disconnect;
    Form1.Enabled := True;
  end;
end;
0

A już zdążyłem nagrać filmik... http://sendvid.com/avqs39kt
Faktycznie podana wersja działa bez problemów.
Tylko dlaczego w takim razie mogę się połączyć w podobny sposób z serwerem FTP przy swoich danych i przesłać na niego plik? Co może być przyczyną błędu?

0

Nie mam pojęcia to coś z konfiguracją serwera FTP jak chcesz to daj na PW dane tego FTP nie bój się nic tam nie zrobię to jutro w wolnej chwili zerknę jak to musi być na tym serwerze bo nigdy nie spotkałem się z czymś takim.

3

Gdyby ktoś miał problem to trzeba tak:

function FTPFileExists(IdFtp: TIDFtp; FileName: string): Boolean;
var
  i: Integer;
begin
  result:= False;
  IdFtp.List('', True); //pobieramy liste wszystkich elementow (pozniej odszukamy pasujacy)
  for i:= 0 to IdFTP.DirectoryListing.Count - 1 do
  begin
    if SameText(IdFTP.DirectoryListing.Items[i].FileName, FileName) and //sprawdzamy nazwe (case sensitive bo na linuksie to ma znaczenie) i
      (IdFTP.DirectoryListing.Items[i].ItemType = ditFile) then //czy to plik a nie np. folder lub dowiazanie
    begin
      result:= True;
      break;
    end;
  end;
end;

To nie jest wina Indy a specyfikacji przestarzałych FTP (nowe radzą sobie bez problemu... ale co poradzić jak ktoś używa jakiegoś zabytkowego oprogramowania) które nie obsługują filtrowania jeżeli się pobiera szczegóły o elemencie a nie tylko nazwe (a to w tym przypadku potrzebne aby wiedzieć czy to plik a nie folder).

0

Wszystko działa jak należy :)
Dzięki wielkie za pomoc ;)

0

Ponownie mam problem z tematem połączenia FTP. Zdecydowałem się napisać pod tym tematem, ponieważ wszystko dotyczy się tej samej aplikacji lecz zmiany serwera FTP. Aktualnie łącze się przez połączenie tunelowane, jednakże podczas podania poprawnych danych logowania i zmiany połączenia na pasywne wysyłanie plików nie dochodzi do skutku z powodu: "Project Project1.exe raised exception class EIdReplyRFCError with message 'PASV: Operation not permitted'. Process stopped. Use Step or Run to continue."

W kodzie wskazuje część (IdReplyRFC):

procedure TIdReplyRFC.RaiseReplyError;
begin
  raise EIdReplyRFCError.CreateError(NumericCode, Text.Text);
end;

Po wyłączeniu połączenia pasywnego wyrzuca zaś błąd: Illegal PORT command.
Miał ktoś z Was może podobny problem?

0

Kod przede wszystkim kiedy ustawiasz Passive na True? Przed logowaniem czy już po?

1 użytkowników online, w tym zalogowanych: 0, gości: 1