2015-07-21 1 views
5

Je manque régulièrement les derniers kb d'un fichier que j'essaie de copier en utilisant shutil copyfile.Python shutil copyfile - manque les dernières lignes

je fait quelques recherches et ne voient quelqu'un à propos de quelque chose de semblable demander ici: python shutil copy function missing last few lines

Mais j'utilise copyfile, qui ne semble utiliser avec une déclaration ...

with open(src, 'rb') as fsrc: 
    with open(dst, 'wb') as fdst: 
     copyfileobj(fsrc, fdst) 

Je Je suis perplexe que plus d'utilisateurs n'ont pas ce problème, si en effet c'est une sorte de problème de mise en mémoire tampon - je pense que ce serait plus connu.

J'appelle copyfile très simplement, ne pense pas que je pourrais peut-être en train de faire quelque chose de mal, en faisant essentiellement les choses comme je pense que la norme:

copyfile(target_file_name,dest_file_name) 

Pourtant, je suis absent la dernière 4kb ou si la fichier chaque fois.

J'ai pas touché la fonction copyfile qui est appelée à shutil qui est ...

def copyfileobj(fsrc, fdst, length=16*1024): 
    """copy data from file-like object fsrc to file-like object fdst""" 
    while 1: 
     buf = fsrc.read(length) 
     if not buf: 
      break 
     fdst.write(buf) 

Je suis à une perte, mais je suppose que je suis sur le point d'apprendre quelque chose sur le rinçage, mise en mémoire tampon, ou avec la déclaration, ou ... Aidez! grâce


à Anand: Anand, j'évité de mentionner ce genre de choses avant notre ère, il est mon sens que ce n'est pas le problème, mais puisque vous avez demandé ... résumé est que je suis Récolter un fichier à partir d'un FTP, la vérification si le fichier est différent de la dernière fois que j'ai enregistré une copie, si c'est le cas, téléchargez le fichier et enregistrez une copie. C'est un code de spaghetti détourné et il a été écrit quand j'étais un novice utilitaire vraiment pur d'un codeur je suppose. Il ressemble à:

for filename in ftp.nlst(filematch): 
    target_file_name = os.path.basename(filename) 
    with open(target_file_name ,'wb') as fhandle: 
    try: 
     ftp.retrbinary('RETR %s' % filename, fhandle.write) 
     the_files.append(target_file_name) 
     mtime = modification_date(target_file_name) 
     mtime_str_for_file = str(mtime)[0:10] + str(mtime)[11:13] + str(mtime)[14:16] + str(mtime)[17:19] + str(mtime)[20:28]#2014-12-11 15:08:00.338415. 
     sorted_xml_files = [file for file in glob.glob(os.path.join('\\\\Storage\\shared\\', '*.xml'))] 
     sorted_xml_files.sort(key=os.path.getmtime) 
     last_file = sorted_xml_files[-1] 
     file_is_the_same = filecmp.cmp(target_file_name, last_file) 
     if not file_is_the_same: 
      print 'File changed!' 
      copyfile(target_file_name, '\\\\Storage\\shared\\'+'datebreaks'+mtime_str_for_file+'.xml') 
     else: 
      print 'File '+ last_file +' hasn\'t changed, doin nothin' 
      continue 
+0

Pouvez-vous montrer plus de votre code, comment vous créez le 'target_file_name', ainsi que la façon dont vous créez le target_file lui-même? –

+0

Y at-il un fichier spécifique avec lequel il arrive toujours? quel os êtes-vous et quel python? pouvez-vous poster un fichier avec lequel il le fait toujours? essayez-vous d'écrire sur un lecteur réseau ou quelque chose? –

+0

Anand, je vous ai répondu dans le post, n'était pas sûr de savoir comment faire d'autres bc trop de caractères pour un commentaire. – 10mjg

Répondre

4

La question serait très probablement ici est que, lors de l'exécution de la ligne -

ftp.retrbinary('RETR %s' % filename, fhandle.write) 

Cette utilise la fonction fhandle.write() pour écrire les données à partir du serveur ftp au fichier (avec le nom - target_file_name), mais au moment où vous appelez - shutil.copyfile - le tampon pour fhandle n'a pas complètement vidé, donc vous manquez certaines données lors de la copie du fichier.Pour vous assurer que cela ne se produit pas, vous pouvez déplacer la logique copyfile du bloc with pour fhandle.

Vous pouvez également appeler le fhandle.flush() pour vider le tampon avant de copier le fichier.

Je crois qu'il serait préférable de fermer le fichier (sortir la logique du bloc with). Exemple -

for filename in ftp.nlst(filematch): 
    target_file_name = os.path.basename(filename) 
    with open(target_file_name ,'wb') as fhandle: 
     ftp.retrbinary('RETR %s' % filename, fhandle.write) 
    the_files.append(target_file_name) 
    mtime = modification_date(target_file_name) 
    mtime_str_for_file = str(mtime)[0:10] + str(mtime)[11:13] + str(mtime)[14:16] + str(mtime)[17:19] + str(mtime)[20:28]#2014-12-11 15:08:00.338415. 
    sorted_xml_files = [file for file in glob.glob(os.path.join('\\\\Storage\\shared\\', '*.xml'))] 
    sorted_xml_files.sort(key=os.path.getmtime) 
    last_file = sorted_xml_files[-1] 
    file_is_the_same = filecmp.cmp(target_file_name, last_file) 
    if not file_is_the_same: 
     print 'File changed!' 
     copyfile(target_file_name, '\\\\Storage\\shared\\'+'datebreaks'+mtime_str_for_file+'.xml') 
    else: 
     print 'File '+ last_file +' hasn\'t changed, doin nothin' 
     continue 
+0

Merci Anand ce correctif était certainement le premier et le plus direct correctif pour le symptôme que j'avais. Je considérerai également la solution de nsilent22 qui semble être plus fondamentalement saine. Toute pensée bienvenue. – 10mjg

+1

nslient22 est la même solution, pour fermer le fichier avant de l'envoyer à la fonction 'copyfile()', en déplaçant le fichier de copie (et toute la logique qui ne dépend pas du gestionnaire de fichier) en dehors du bloc 'with '. –

+0

Ah vous avez raison, je me suis concentré sur l'idée de «flush» - mais vous avez également spécifié «Pour vous assurer que cela ne se produit pas, vous pouvez soit déplacer la logique du fichier de copie du bloc with fhandle. Merci beaucoup. – 10mjg

1

This ressemble il y a une meilleure façon de le faire emboîtés withs:

with open(src, 'rb') as fsrc, open(dst, 'wb') as fdst: 
     copyfileobj(fsrc, fdst) 

Je vais essayer quelque chose comme ça. Je suis loin d'être un expert, j'espère que quelqu'un de mieux informé pourra nous donner un aperçu. Ma meilleure idée est que l'intérieur with se ferme avant l'extérieur.

+0

Je serais heureux d'essayer/tester cela. Peut-être que je suis naïf, mais n'est-ce pas étrange, mais que ... shutil copyfile pourrait être utilisé si largement et encore être sous-optimal/besoin de ce correctif? – 10mjg

+0

Oui, c'est bizarre. Je vais honnêtement être un peu surpris si cela le corrige, mais les blocs 'with' me gâchent toujours. J'utilise 'open()' et 'close()' comme un noob :) – Will

2

Vous essayez de copier un fichier qui n'a pas été fermé. C'est pourquoi les tampons n'ont pas été vidés. Déplacez le copyfileobj du bloc with pour permettre la fermeture de fhandle.

Do:

with open(target_file_name ,'wb') as fhandle: 
    ftp.retrbinary('RETR %s' % filename, fhandle.write) 

# and here the rest of your code 
# so fhandle is closed, and file is stored completely on the disk 
+0

Je vais jeter un coup d'oeil à ceci juste après avoir fini de tester la solution de rinçage d'Anand que j'ai attrapée plus tôt et qui semblait aussi la réparer. Je ne suis pas assez intelligent pour savoir quelle est la meilleure solution/etc. Le vôtre semble être une solution plus fondamentale. Merci et toute autre idée est appréciée. – 10mjg

+0

Cela fonctionne également après un peu de test. Wow, si évident avec le recul. Je vous remercie. – 10mjg

+0

@ 10mjg: Vous êtes les bienvenus;) – nsilent22