Bonjour,
Je voudrais savoir s'il est possible de lire un fichier par ftp sans le copier?
Merci
Bonjour,
Je voudrais savoir s'il est possible de lire un fichier par ftp sans le copier?
Merci
Bonjour,
Il faudra, bien entendu, que le contenu du fichier ftp soit téléchargé! Le seule chose qu'on peut essayer d'éviter, c'est l'enregistrement sur disque.
On devrait pouvoir le faire avec un fichier virtuel du module "io" (Python 3). Pour un fichier texte, la classe "StringIo" devrait convenir. Pour un fichier binaire, voir les autres classes de ce module. Avec Python 2, StringIo était un module.
Bonjour,
Comme je n'avais jamais fait ça, et que je suis curieux, j'ai essayé:
On ne détaillera pas ici l'ouverture et la fermeture de la connexion FTP
Bien sûr, s'il faut afficher le fichier texte dans un éditeur de texte de l'OS, il y a un petit problème: les éditeurs demandent en général un fichier sur disque. On pourrait peut-être passer par un copier-coller, mais il y a une autre possibilité tout de même: utiliser une bibliothèque graphique: en accédant à un éditeur directement par son code, on pourrait lui donner une chaine de caractères à afficher. Mais ça complique sérieusement le développement...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 from io import BytesIO # ouvre la connexion FTP => ftp # adresse du fichier sur le serveur FTP fichierftp = "adresse/du/fichier/sur/le/serveur/FTP" # ouvre un fichier virtuel en mémoire flocal = BytesIO() # télécharge le fichier sous forme binaire (donc => bytes): ftp.retrbinary('RETR ' + fichierftp, flocal.write) # convertit les bytes en strings (il faut connaître l'encodage) texte = str(flocal.getvalue(), encoding='utf-8') # affiche les lignes de texte for ligne in texte.splitlines(): print(ligne) # ferme le fichier virtuel flocal.close() # ferme la connexion FTP
Bonjour,
Je souhaite juste lire un fichier par ftp (sans le télécharger, ni rien d'autre... juste ouvrir, lire puis fermer) et stocker ses données dans un array pour les traiter, ça n'a pas l'air trè compliqué mais ça m'occupe...
Bonjour,
Je pensais avoir répondu à cette question, aussi, j'insiste: tu ne peux pas visualiser le contenu du fichier sans le télécharger! et encore moins récupérer ses données dans un array. Tu peux juste éviter son enregistrement sur disque local: il reste alors en mémoire pour traitement. Si c'est pour aller plus vite, peut-être qu'un disque virtuel en RAM serait plus adapté?
Juste pour ma culture personnelle, pourquoi je ne peux pas par ftp ouvrir un fichier, le lire ligne par ligne (en parallele stocker chaque ligne lue dans un array), ensuite fermer le fichier?
Dans un autre language de programmation, j'arrive à faire cela sans avoir à le télécharger ou stocker quelque part... sauf si c'est moi qui ne comprend rien à la philosophie ftp.
FTP est un protocole Internet et ce qu'il fait (ou pas) n'a rien à voir avec le langage de programmation.
Maintenant, rien n'empêche de coder une fonctionnalité pour rendre transparent le chargement d'un tableau à partir d'un téléchargement distant fait via FTP. Par exemple avec numpy, vous avez la fonctionnalité de DataSource.
- W
Si je comprend bien, flocal est un fichier virtuel? il n'est écrit nul par c'est ça?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 from io import BytesIO # ouvre la connexion FTP => ftp # adresse du fichier sur le serveur FTP fichierftp = "adresse/du/fichier/sur/le/serveur/FTP" # ouvre un fichier virtuel en mémoire flocal = BytesIO() # télécharge le fichier sous forme binaire (donc => bytes): ftp.retrbinary('RETR ' + fichierftp, flocal.write) # convertit les bytes en strings (il faut connaître l'encodage) texte = str(flocal.getvalue(), encoding='utf-8') # affiche les lignes de texte for ligne in texte.splitlines(): print(ligne) # ferme le fichier virtuel flocal.close() # ferme la connexion FTP
Là, tu précises ton problème: tu veux lire et traiter ligne par ligne un fichier texte. Ça, ce sera plus simple:
On utilise retrlines du module ftplib, qui TELECHARGERA ligne par ligne et enverra chaque ligne comme argument de la fonction "traiteligne" qui pourra afficher, stocker dans un tableau, etc...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 def traiteligne(ligne): ligne = ligne.rstrip() # pour retirer la fin de ligne print(ligne) # ouvre la connexion FTP => ftp # adresse du fichier sur le serveur FTP fichierftp = "/chemin/tonfichiertexte.txt" # télécharge le fichier texte ligne par ligne: ftp.retrlines('RETR ' + fichierftp, traiteligne) # ferme la connexion FTP
A voir comment on traite les pb d'encodage (je n'ai plus le temps maintenant: dis si tu as ce pb)
J'ai essayé ta fonction qui marche bien pour le moment
Par contre j'ai ce défaut là au bout de 2 minutes: (fermeture automatique de la connexion ftp?):
Ci-joint mon code complet:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 ftp.retrbinary('RETR ' + self.tree.item(curItem)['values'][2], LOCAL_FILE.write) File "C:\Python_3.7\lib\ftplib.py", line 441, in retrbinary self.voidcmd('TYPE I') File "C:\Python_3.7\lib\ftplib.py", line 278, in voidcmd return self.voidresp() File "C:\Python_3.7\lib\ftplib.py", line 251, in voidresp resp = self.getresp() File "C:\Python_3.7\lib\ftplib.py", line 244, in getresp raise error_temp(resp) ftplib.error_temp: 421 Timeout (120 seconds): closing control connection.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250 from configparser import ConfigParser import tkinter as tk from tkinter import * from tkinter import ttk from tkinter.ttk import * from tkinter import font from tkinter import Listbox import ftplib import os from datetime import datetime, timedelta from pytz import timezone import string import array from io import BytesIO import numpy #Application parameters #FTP parametrers CFG_FILE = ConfigParser() CFG_FILE.optionxform = str CFG_FILE.read('ini/application.ini') print(os.getlogin()+' connected') APPLICATION_NAME = CFG_FILE.get('APPLICATION_PARAMETERS', 'APPLICATION_NAME') LINE_NAME = CFG_FILE.options('NETWORK_PARAMETERS') NUMBER_OF_LINE = len(LINE_NAME) global FTP_USER, FTP_PASSWORD, DELTA_TIME, Cell_folder FTP_USER = CFG_FILE.get('SECRET', 'USER') FTP_PASSWORD = CFG_FILE.get('SECRET', 'PASSWORD') DELTA_TIME = CFG_FILE.get('FTP_PARAMETERS', 'DeltaTime') class Application(ttk.Frame): def __init__(self, main_window): super().__init__(main_window) main_window.title(APPLICATION_NAME) main_window.iconbitmap(r'ini/graph_icon.ico') main_window.geometry('1200x700') Mainfont = font.Font(main_window, family='Courier new', size='10') self.LabelOfLine = ttk.Label(main_window, text="Select a line:", font=Mainfont) self.LabelOfLine.place(x=5, y=10) self.Combo_line = ttk.Combobox(values=LINE_NAME, state="readonly", font=Mainfont) self.Combo_line.place(x=5, y=30) self.Combo_line.bind('<<ComboboxSelected>>', self.FTP_GET_folder) self.LabelOfLine = ttk.Label(main_window, text="Select a cell:", font=Mainfont) self.LabelOfLine.place(x=5, y=60) self.Cell_listbox = Listbox(main_window, font=Mainfont, height=16, width=22) self.Cell_listbox.place(x=5, y=80) self.Cell_listbox.bind('<Double-1>', self.FTP_GET_FilesIntoFolder) self.LabelOfFile = ttk.Label(main_window, text="Select a file:", font=Mainfont) self.LabelOfFile.place(x=200, y=60) self.tree = ttk.Treeview(main_window) style = ttk.Style() style.configure("Treeview.Heading", font=('Courier new', 10)) style.configure("Treeview", font=('Courier new', 10)) self.tree["columns"] = ("one","three", "four") self.tree.column("#0", width=230, minwidth=0, stretch=tk.NO) self.tree.column("one", width=190, minwidth=0, stretch=tk.NO) #self.tree.column("two", width=400, minwidth=200) self.tree.column("three", width=100, minwidth=0, stretch=tk.NO) self.tree.column("four", width=400, minwidth=0, stretch=tk.NO) self.tree.heading("#0",text="Nom",anchor=tk.W) self.tree.heading("one", text="Date",anchor=tk.W) #self.tree.heading("two", text="Type",anchor=tk.W) self.tree.heading("three", text="Taille",anchor=tk.W) self.tree.heading("four", text="Chemin",anchor=tk.W) self.tree.place(x=200, y=80, height=275 , width=930) self.tree.bind('<Double-1>', self.FTP_OpenSelectedFile) self.place(width=300, height=200) self.NumberOfFile = StringVar() #Doesn't operate... why i don't know yet self.NumberOfFile = ('DEFAULT_NUMBER_OF_FILE') self.LabelNumberOfFile = ttk.Label(main_window, text=self.NumberOfFile, font=Mainfont) self.LabelNumberOfFile.place(x=200, y=30) def FTP_GET_folder(self, event): global SELECTED_LINE, SERVER_NAME, ftp SELECTED_LINE = self.Combo_line.get() print('Line '+SELECTED_LINE+' selected') FTP_PARAMETERS = CFG_FILE.get('NETWORK_PARAMETERS', SELECTED_LINE) SERVER_NAME, PORT, FTP_PATH = FTP_PARAMETERS.split(";") ftp = ftplib.FTP() ftp.connect(SERVER_NAME, int(PORT)) ftp.login(FTP_USER, FTP_PASSWORD) Names = ftp.nlst(FTP_PATH) CurrentDir = ftp.pwd() SubDirs = [] for name in Names: chemin = name okdir = self.GET_Only_directories(ftp, chemin) if okdir: SubDirs.append(name) self.Cell_listbox.delete(0, END) for SubDir in SubDirs: SubDirSplit = SubDir.split("/") LenOfSubDirSplit = len(SubDirSplit) self.Cell_listbox.insert(END, SubDirSplit[LenOfSubDirSplit-1]) #ftp.close() def GET_Only_directories(self, ftp, chemin): CurrentDir = ftp.pwd() try: ftp.cwd(chemin) ftp.cwd(CurrentDir) return True except Exception: return False def FTP_GET_FilesIntoFolder(self, event): global FTP_PATH, Cell_folder try: index = self.Cell_listbox.curselection()[0] Cell_folder = self.Cell_listbox.get(index) print('Cell '+Cell_folder+' selected') for i in self.tree.get_children(): self.tree.delete(i) FTP_PARAMETERS = CFG_FILE.get('NETWORK_PARAMETERS', SELECTED_LINE) SERVER_NAME, PORT, FTP_PATH = FTP_PARAMETERS.split(";") ftp = ftplib.FTP() ftp.connect(SERVER_NAME, int(PORT)) ftp.login(FTP_USER, FTP_PASSWORD) ftp.cwd(FTP_PATH+Cell_folder) files = [] iFile = 0 FilteredFiles = [] files = ftp.nlst() #Here the exclude files for file in files: if file.split('.')[-1] == 'txt': continue elif file.split('.')[-1] == 'io': continue else: FilteredFiles.append(file) #Here all files with filters for i in FilteredFiles: self.DT_convertion(ftp, i) self.tree.insert('', 'end', text=i, values=(self.DT_convertion(ftp, i), ftp.size(i), FTP_PATH+Cell_folder+'/'+i)) iFile +=1 if iFile==1: print('One file found') self.NumberOfFile.set("One file found") #Doesn't operate... why i don't know yet elif iFile>1: print(iFile, 'files found') self.NumberOfFile.set(str(iFile) + " files found") #Doesn't operate... why i don't know yet else: print('No file found') self.NumberOfFile.set("No file found") #Doesn't operate... why i don't know yet return True except Exception: return False #Here date time of file according local (set in config file) def DT_convertion(self, ftp, i): utc_dt = ftp.sendcmd('MDTM '+i)[4:] a, m, j = int(utc_dt[:4]), int(utc_dt[4:6]), int(utc_dt[6:8]) h, mn, s = int(utc_dt[8:10]), int(utc_dt[10:12]), int(utc_dt[12:]) local_dt = datetime(a, m, j, h, mn, s) local_dt = local_dt+timedelta(hours=int(DELTA_TIME)) return local_dt.isoformat(sep=' ') #Here opening of selected file in ListView def FTP_OpenSelectedFile(self, event): curItem = self.tree.focus() LOCAL_FILE = BytesIO() ftp.retrbinary('RETR ' + self.tree.item(curItem)['values'][2], LOCAL_FILE.write) TEXT = str(LOCAL_FILE.getvalue(), encoding='utf-8') FILE_CONTENT = [] iLine = 0 for LINE in TEXT.splitlines(): #print(LINE) FILE_CONTENT.append(LINE.replace("'", "").strip()) iLine+=1 # ferme le fichier virtuel LOCAL_FILE.close() #Search of the empty line-------------------- COUNT_LINE_BEFORE_EMPTY=0 #print(iLine) for READ_ROW in FILE_CONTENT: if READ_ROW.strip() != "" : COUNT_LINE_BEFORE_EMPTY+=1 else: break #Search of the mm;kN line-------------------- STANDARD_CURVE_INDEX = [] CALIBRATION_CURVE = [] START_OF_DATA_CURVE = 1 for READ_ROW in FILE_CONTENT: if READ_ROW.strip() != "mm;kN": START_OF_DATA_CURVE+=1 elif READ_ROW.strip() != "mm;kN;kN": START_OF_DATA_CURVE+=1 else: break print(START_OF_DATA_CURVE) #print(COUNT_LINE_BEFORE_EMPTY) #End of search------------------------------- #Get Header of curve file #Character remplacement---------------------- a=0 while a <= COUNT_LINE_BEFORE_EMPTY: print(FILE_CONTENT[a].replace(";", "")) a+=1 #Do not delete below: #Character substitution DATA_CURVE = [] # print(COUNT_LINE_BEFORE_EMPTY) # print(iLine) for i in range(COUNT_LINE_BEFORE_EMPTY, iLine): print(FILE_CONTENT[i]) main_window = tk.Tk() app = Application(main_window) app.mainloop()
En gros:
-FTP_GET_folder(): Liste les sous-dossiers d'un dossier selon choix ComboBox,
-FTP_GET_FilesIntoFolder(): Liste des fichiers contenus dans le dossier sélectionné dans la ListBox,
-FTP_OpenSelectedFile(): Ouvre le fichier sélectionné dans la ListView...
Pour en revenir à mon problème de timeout, c'est effectivement bizarre... c'est surtout balot si au bout de 2 minutes si je laisse mon application ouverte je sois obligé de refaire une sélection ComboBox, etc...
ok, mais alors là ce n'est pas gagné... je pense que dans mon code ci-dessus la connexion est sûrement améliorable, mais vu que je débute en python c'est pas chose aisée pour moi...
Bonjour,
Je viens d'essayer un truc: j'ai créé un gros fichier texte de 4Go avec 100.000 lignes au hasard, je l'ai mis sur mon serveur FTP, et je l'ai relu ligne par ligne avec ftp.retrlines: aucun problème, les 100.000 lignes sont lues sans erreur.
J'ai ensuite ajouté sleep(0.1) dans la fonction qui traite chaque ligne pour simuler une charge de traitement. Cela ralentit sérieusement la lecture, mais ça ne donne aucune erreur. Idem si je monte à sleep(1) (=1 seconde entre chaque ligne!).
Ce n'est donc pas le code Python qui pose problème, mais la partie réseau.
Je te propose quelque chose pour avancer: ajoute la ligne suivante avant ftp.retrlines:
Avec ce code, les lignes spécifiques au protocole FTP seront ajoutées aux lignes du fichier. L'intéret est que lorqu'il y aura une erreur, on verra ce que le protocole FTP renverra, et donc la cause de l'erreur. Par exemple, avec une simulation d'une erreur de timout, les dernières lignes renvoyées chez moi sont:
Code : Sélectionner tout - Visualiser dans une fenêtre à part ftp.set_debuglevel(2)
Et les codes FTP renvoyés entre 400 et 499 sont bien des problèmes de temporisation (exception ftplib.error_temp).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 *cmd* 'QUIT' *put* 'QUIT\r\n' *get* '450 Transfer aborted. Link to file server lost\n' *resp* '450 Transfer aborted. Link to file server lost'
Voici le résultat:
C'est grave Docteur?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 *cmd* 'TYPE I' *put* 'TYPE I\r\n' *get* '421 Timeout (120 seconds): closing control connection.\n' *resp* '421 Timeout (120 seconds): closing control connection.'
Comme c'est manifestement un timeout, tu peux essayer d'en mettre un comme argument à ftplib.FTP():
Peut-être que, sans ça, ton serveur n'accepte pas qu'un fichier soit ouvert plus de 2 minutes.
Code : Sélectionner tout - Visualiser dans une fenêtre à part ftp = ftplib.FTP(timeout=1200) # soit 20 minutes
Bon même punition, je pense que ça dois provenir de mon code alors...
Partager