Как загрузить большой файл с помощью модуля Python

#python #api #rest #web

Вопрос:

Текущая проблема:

  1. Пользователь может войти в https://<ПОЛНОЕ доменное имя>/Пользовательский интерфейс с помощью веб-браузера, затем щелкнуть где-нибудь и загрузить файл, например, файл размером 2,5 G. Все эти операции(вход,…, загрузка) являются реальными вызовами API.
  2. Кроме того, пользователь также может напрямую выполнять вызовы Rest API с помощью приложений Java или Python. Итак, я пишу фрагмент кода Python, чтобы сделать это. К сожалению, в результате успешного вызова API «https://<ПОЛНОЕ доменное имя>/api/логин», следующий вызов API «https://<ПОЛНОЕ ДОМЕННОЕ ИМЯ><ПОЛНОЕ доменное ИМЯ>/api/v1.0/Файлы обновления/загрузка» имеет проблемы. Он может загружать только небольшие файлы. Для больших файлов, таких как 2,5 Г, это не удается.
  3. Когда я возвращаюсь в веб-браузер и пытаюсь загрузить большой файл с окном отладки, открытым F12, я вижу, что он выполняет несколько запросов, и в каждом заголовке запроса он содержит, как показано ниже:
     content-length : 5000464
    sec-ch-ua : " Not A;Brand";v="99", "Chromium";v="90", "Microsoft Edge";v="90"
    sec-ch-ua-mobile : ?0
    user-agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36 Edg/90.0.818.46
    content-type : multipart/form-data; boundary=----WebKitFormBoundaryPuIjpBG8n874gC29
    content-range : bytes 0-4999999/2684354560
    accept : application/json
    x-requested-with : XMLHttpRequest
    content-disposition : attachment; filename="b86aee78-56db-4398-8cd0-69b156740a2a"
     

    Похоже, что он разделяет весь файл, устанавливает «диапазон содержимого» и выполняет вызовы API.

  4. Я хочу смоделировать поведение веб-браузера, поэтому я пишу фрагмент кода Python, как показано ниже:
     #_*_ coding:utf-8 _*_
    import requests
    import time
    import random
    import string
    from requests_toolbelt.multipart import encoder
    
    datas = {'userName':'<Username>','<Password>'}
    header = {'accept':'application/json', 'Content-Type':'application/json'}
    
    r = requests.post('https://<FQDN>/api/login', headers=header, data=datas)
    #print(r.content)
    print(r.status_code)
    if r.status_code == 200:
        print("Login is successful")
    responseBody = r.json()
    sessionId = responseBody['sessionId']
    print(sessionId)
    
    url = "https://<FQDN>/api/v1.0/updateFiles/upload"
    
    
    fields = {
        'Content-Dispostion':'attachment; filename="3rdParty1.tgz"',
        'file': ('1.tar.gz', open('C:/Users/Jie2/Downloads/1.tar.gz', 'rb'), 'application/zip')
    }
    
    boundary = '----WebKitFormBoundary'   ''.join(random.sample(string.ascii_letters string.digits,16))
    
    multipart_encoder = encoder.MultipartEncoder(fields=fields,boundary=boundary)
    
    
    headers = {
        'accept':'application/json',
        'sessionId':sessionId,
        'Referer':'https://<FQDN>/UI/',
        'Content-Type': 'multipart/form-data; boundary={}'.format(boundary),
    
    }
    
    response = requests.post(url=url, headers=headers,
                             data=multipart_encoder,
                             cookies=r.cookies)
    
    print(response)
     

    Но приведенный выше код может загружать только небольшие файлы, такие как кбайты. Для больших файлов, таких как 2,5 Г, это не удается.
    Приведенный выше код принимает https://toolbelt.readthedocs.io/en/latest/uploading-data.html в качестве ссылки.

  5. Я также попытался отказаться от requests_toolbelt и использовать только модуль запросов, а также использовать цикл для ручного чтения файла по разделам и ручной настройки «диапазона содержимого». К сожалению, это тоже не работает.
     def mutiple_upload(str,f,str1):
        # m1 = md5()
        # m1.update(Bfile)
        hx = {
            "Host": "<FQDN>",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
            "Accept": "application/json",
            "Accept-Language": "en-US,en;q=0.9",
            "Accept-Encoding": "gzip, deflate, br",
            "Referer": "https://<FQDN>/ui",
            "X-Requested-With": "XMLHttpRequest",
            "Content-Length": "75023",
            "Connection": "keep-alive",
            "Transfer-Encoding": 'chunked',
            "sessionId": sessionId,
            "timestamp": now2,
            "Content-MD5": str1,
            "Content-Range": str
        }
        resp=requests.post(url, headers=hx, files=f)
        # print(resp.json())
    
    for x in range(0,t):
        m = md5()
        if ((x x*chunk_size chunk_size) < fLen):
            crf= "bytes "   str(x x*chunk_size)   "-"   str(x x*chunk_size chunk_size)  "/"   str(fLen)
            f = open('<FileName>', 'rb')
            f.seek((x x*chunk_size),0)
            fs = f.read(chunk_size)
            fx = {
                'name': (None, '<FileName>'),
                'file': ('<FileName>',fs)}
            m.update(fs)
            mutiUpload = mutiple_upload(crf, fx, str(m.hexdigest()))
        else:
            crf = "bytes "   str(x   x * chunk_size)   "-"   str(fLen - 1)   "/"   str(fLen)
            f = open('<FileName>', 'rb')
            f.seek((x x*chunk_size),0)
            fs = f.read(chunk_size - 1)
            fx = {
                'name': (None, '<FileName>'),
                'file': ('<FileName>',fs)}
            m.update(fs)
            mutiUpload = mutiple_upload(crf,fx, str(m.hexdigest()))
     

Does anybody know if there is a python module that can help me to implement this requirement — automatically read the large file section by section, and automatically set the header like «content-range» to make it work? Or, I do not find a correct way to use requests_toolbelt.