Je veux transférer un gros fichier avec adb pull
, mais ma connexion USB se déconnecte tout le temps, interrompant le transfert. Comment puis-je faire en sorte que cela fonctionne?
Réponses
Trop de publicités?Voici un script Python 3 qui implémente une solution de contournement basée sur adb
et dd
. Il réessaie en continu et reprend le téléchargement en cas de déconnexion.
#!/usr/bin/env python3
# adb-repull.py
#
# Émulation de la commande ADB pull pour les machines avec des ports/câbles USB problématiques.
# Il réessaie en continu et reprend le téléchargement en cas de déconnexion.
#
# Droits d'auteur (c) 2018 Alexander Lopatin
#
# La permission est accordée, à titre gratuit, à toute personne obtenant une copie
# de ce logiciel et des fichiers de documentation associés (le "Logiciel") pour traiter
# dans le Logiciel sans restriction, y compris, sans limitation, les droits
# d'utiliser, de copier, de modifier, de fusionner, de publier, de distribuer, de concéder sous licence et / ou de vendre
# des copies du Logiciel, et de permettre aux personnes à qui le Logiciel est
# fourni de le faire, sous réserve des conditions suivantes:
#
# L'avis de droit d'auteur ci-dessus et cet avis d'autorisation doivent être inclus dans tous
# les copies ou portions substantielles du Logiciel.
#
# LE LOGICIEL EST FOURNI "TEL QUEL", SANS GARANTIE D'AUCUNE SORTE, EXPRESSE OU
# IMPLICITE, Y COMPRIS MAIS SANS S'Y LIMITER LES GARANTIES DE QUALITÉ MARCHANDE,
# ADAPTATION À UN USAGE PARTICULIER ET ABSENCE DE CONTREFAÇON. EN AUCUN CAS LE
# AUTEURS OU TITULAIRES DE DROITS D'AUTEUR NE SAURAIENT ÊTRE TENUS RESPONSABLES DE TOUTE RÉCLAMATION, DOMMAGES OU AUTRES
# RESPONSABILITÉ, QUE CE SOIT DANS UNE ACTION DE CONTRAT, DE TORT OU AUTRE, DÉCOULANT DE,
# HORS DE OU EN LIAISON AVEC LE LOGICIEL OU L'UTILISATION OU AUTRE TIERS DANS LE
# LOGICIEL.
import errno
import math
import os
import subprocess
import sys
import time
BUFFER_SIZE = 8192
SLEEP_TIMEOUT = 5
def run_with_retries(function):
while True:
try:
return function()
except ConnectionError as e:
print(e)
print('Nouvel essai après {} secondes'.format(SLEEP_TIMEOUT))
time.sleep(SLEEP_TIMEOUT)
def size_to_blocks(size):
return math.ceil(size / BUFFER_SIZE)
def get_size(remote_file):
print('Obtention de la taille de {}'.format(remote_file))
with subprocess.Popen(['adb', 'shell', 'du', '-b', remote_file], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as process:
out, err = process.communicate()
if len(err) > 0:
raise ConnectionError('Déconnecté')
size_and_remote_file = out.decode('utf-8').split()
if len(size_and_remote_file) > 0:
return int(size_and_remote_file[0])
else:
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), remote_file)
def is_execout_supported():
print('Vérification si exec-out est pris en charge')
with subprocess.Popen(['adb', 'exec-out', 'echo'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as process:
out, err = process.communicate()
if len(err) > 0 and err.decode('utf-8').strip() != 'erreur : fermé':
raise ConnectionError('Déconnecté')
result = process.returncode == 0
print('Oui' if result else 'Non')
return result
def get_size_with_retries(remote_file):
return run_with_retries(lambda: get_size(remote_file))
def is_execout_supported_with_retries():
return run_with_retries(is_execout_supported)
def update_progress(remote_file, current_block, last_block, speed):
if current_block % 1000 == 0 or current_block == last_block:
progress = (current_block / last_block) * 100.0
speed_in_mib = speed / (1024 * 1024)
print('Téléchargement de {} {:.1f}% ({:.1f} MiB/s)'.format(remote_file, progress, speed_in_mib))
def pull(remote_file, local_file, remote_size, execout, output):
print('Téléchargement de {}'.format(remote_file))
last_block = size_to_blocks(remote_size)
local_size = os.path.getsize(local_file)
current_block = size_to_blocks(local_size)
time_elapsed = 0
bytes_downloaded = 0
dd_command = "dd if={} bs={} skip={} 2>>/dev/null".format(remote_file, BUFFER_SIZE, current_block)
command = ['adb', 'exec-out', dd_command] if execout else ['adb', 'shell', 'busybox stty raw ; {}'.format(dd_command)]
with subprocess.Popen(command, stdout=subprocess.PIPE) as process:
while current_block < last_block:
time_start = time.time()
current_block += 1
expected_buffer_size = remote_size - local_size if current_block == last_block else BUFFER_SIZE
buffer = process.stdout.read(expected_buffer_size)
buffer_size = len(buffer)
if buffer_size != expected_buffer_size:
raise ConnectionError('Mauvaise taille du tampon {}. Déconnecté'.format(buffer_size))
output.write(buffer)
local_size += buffer_size
time_end = time.time()
time_elapsed += time_end - time_start
bytes_downloaded += buffer_size
speed = bytes_downloaded / time_elapsed
update_progress(remote_file, current_block, last_block, speed)
print('Terminé')
def pull_with_retries(remote_file, local_file):
remote_size = get_size_with_retries(remote_file)
execout = is_execout_supported_with_retries()
with open(local_file, 'a+b') as output:
output.seek(os.SEEK_END)
run_with_retries(lambda: pull(remote_file, local_file, remote_size, execout, output))
def main(argv):
if len(argv) < 2:
print('Utilisation: %s /mnt/sdcard/remote.bin [local.bin]' % argv[0])
else:
try:
if sys.platform != 'linux':
raise OSError('Plateforme non prise en charge')
remote_file = argv[1]
local_file = os.path.basename(remote_file) if len(argv) < 3 else argv[2]
pull_with_retries(remote_file, local_file)
except OSError as e:
print(e)
main(sys.argv)
Pour moi, les performances sont les mêmes que pour adb pull
. Si vous rencontrez des problèmes de performance, essayez de jouer avec la valeur de BUFFER_SIZE
.
Vous avez quelques options ici si votre connexion continue à se couper.
-
Utilisez le script de alopatindev pour reprendre l'utilisation de la commande
dd
. -
Utilisez ADB sans fil via le Wi-Fi. La connexion peut être un peu plus lente. C'est plus facile, mais s'arrêtera également en cas de déconnexion du WiFi car c'est la même chose qu'ADB. Allez dans les options pour les développeurs, activez le Débogage sans fil, puis appuyez sur les mots/flèche "Débogage sans fil". Maintenant vous pouvez démarrer ADB sans fil. Regardez votre IP et port, et entrez-les avec
adb connect IP:port
-
Installez RSync dans Termux (Fdroid). RSync a une option de reprise. Vous devrez être capable de démarrer le serveur SSH sur l'un des appareils (soit avec
systemd
sur l'ordinateur, soit en exécutant directementsshd
sur Termux), avec RSync sur les deux appareils. Vous pouvez le faire sur Windows en utilisant WSL, mais le réseau WSL est assez lent.
ADB ne peut pas extraire des fichiers partiellement ou reprendre. De plus, modifier la partie PC de ADB ne vous sera d'aucune utilité, car la fonctionnalité fournie est gérée par la partie sur l'appareil (que vous ne pouvez pas remplacer).
De mon point de vue, il reste deux possibilités :
-
Installer un serveur FTP (application) qui permet de démarrer automatiquement avec Android et qui est capable de reprendre le téléchargement. Permettez de partager un répertoire, où se trouve le fichier que vous souhaitez obtenir, ou utilisez vos 20 secondes pour déplacer le fichier dans le répertoire partagé FTP.
-
Une autre option serait une application de synchronisation cloud (ou un outil en ligne de commande que vous pourriez appeler via adb) qui est capable de pousser automatiquement des fichiers par blocs vers le serveur (comme le client de bureau DropBox le fait). Malheureusement, à ma connaissance, le client DropBox Android officiel fonctionne différemment et n'est d'aucune utilité dans ce scénario. Mais une application DropBox tierce ou une autre application de synchronisation cloud différente pourrait être utilisable.
0 votes
Je ne crois pas qu'il y ait une option "reprendre" ou continuer...
0 votes
Est-ce qu'il y a un moyen pour moi de modifier le code moi-même ? Est-ce que c'est open source ?
0 votes
Je ne sais pas... et cela est en dehors du cadre de ce forum (les questions spécifiques aux développeurs sont hors-sujet ici).
0 votes
D'accord, je vais juste attendre de voir si quelqu'un connaît une manière ou un "wrapper" qui permet de reprendre
0 votes
Si vous rencontrez des problèmes avec la connexion USB mais que vous disposez d'une connexion Wifi fonctionnelle, vous pouvez activer ADB via TCP et ainsi éviter l'USB.
0 votes
Non, le problème est que mon appareil se réinitialise en boucle après quelques secondes, donc la seule façon à laquelle je peux penser pour faire une sauvegarde est de reprendre.
0 votes
Une boucle de démarrage ne permettra probablement pas suffisamment de temps pour copier des données du tout, je doute donc que cela fonctionne. Quel type d'appareil utilisez-vous, peut-être pourriez-vous le réparer avec une approche différente.
1 votes
Mon boucle dure environ 20 secondes. C'est un Xiaomi, et j'ai vu qu'il y a plus de personnes affectées, avec des temps de boucle similaires. Si je pouvais reprendre le tirage, alors je pourrais tirer des fichiers petit à petit en 20 secondes pour les obtenir tous. Je ne peux pas penser à une autre approche, pour être honnête. Toute suggestion est appréciée.