2008-09-16 31 views

Trả lời

26

Có. Bạn sẽ sử dụng mô-đun urllib2 và mã hóa bằng loại nội dung multipart/form-data. Dưới đây là một số mẫu mã để giúp bạn bắt đầu - đó là nhiều hơn một chút so với chỉ nộp tải lên, nhưng bạn sẽ có thể đọc qua nó và xem làm thế nào nó hoạt động:

user_agent = "image uploader" 
default_message = "Image $current of $total" 

import logging 
import os 
from os.path import abspath, isabs, isdir, isfile, join 
import random 
import string 
import sys 
import mimetypes 
import urllib2 
import httplib 
import time 
import re 

def random_string (length): 
    return ''.join (random.choice (string.letters) for ii in range (length + 1)) 

def encode_multipart_data (data, files): 
    boundary = random_string (30) 

    def get_content_type (filename): 
     return mimetypes.guess_type (filename)[0] or 'application/octet-stream' 

    def encode_field (field_name): 
     return ('--' + boundary, 
       'Content-Disposition: form-data; name="%s"' % field_name, 
       '', str (data [field_name])) 

    def encode_file (field_name): 
     filename = files [field_name] 
     return ('--' + boundary, 
       'Content-Disposition: form-data; name="%s"; filename="%s"' % (field_name, filename), 
       'Content-Type: %s' % get_content_type(filename), 
       '', open (filename, 'rb').read()) 

    lines = [] 
    for name in data: 
     lines.extend (encode_field (name)) 
    for name in files: 
     lines.extend (encode_file (name)) 
    lines.extend (('--%s--' % boundary, '')) 
    body = '\r\n'.join (lines) 

    headers = {'content-type': 'multipart/form-data; boundary=' + boundary, 
       'content-length': str (len (body))} 

    return body, headers 

def send_post (url, data, files): 
    req = urllib2.Request (url) 
    connection = httplib.HTTPConnection (req.get_host()) 
    connection.request ('POST', req.get_selector(), 
         *encode_multipart_data (data, files)) 
    response = connection.getresponse() 
    logging.debug ('response = %s', response.read()) 
    logging.debug ('Code: %s %s', response.status, response.reason) 

def make_upload_file (server, thread, delay = 15, message = None, 
         username = None, email = None, password = None): 

    delay = max (int (delay or '0'), 15) 

    def upload_file (path, current, total): 
     assert isabs (path) 
     assert isfile (path) 

     logging.debug ('Uploading %r to %r', path, server) 
     message_template = string.Template (message or default_message) 

     data = {'MAX_FILE_SIZE': '3145728', 
       'sub': '', 
       'mode': 'regist', 
       'com': message_template.safe_substitute (current = current, total = total), 
       'resto': thread, 
       'name': username or '', 
       'email': email or '', 
       'pwd': password or random_string (20),} 
     files = {'upfile': path} 

     send_post (server, data, files) 

     logging.info ('Uploaded %r', path) 
     rand_delay = random.randint (delay, delay + 5) 
     logging.debug ('Sleeping for %.2f seconds------------------------------\n\n', rand_delay) 
     time.sleep (rand_delay) 

    return upload_file 

def upload_directory (path, upload_file): 
    assert isabs (path) 
    assert isdir (path) 

    matching_filenames = [] 
    file_matcher = re.compile (r'\.(?:jpe?g|gif|png)$', re.IGNORECASE) 

    for dirpath, dirnames, filenames in os.walk (path): 
     for name in filenames: 
      file_path = join (dirpath, name) 
      logging.debug ('Testing file_path %r', file_path) 
      if file_matcher.search (file_path): 
       matching_filenames.append (file_path) 
      else: 
       logging.info ('Ignoring non-image file %r', path) 

    total_count = len (matching_filenames) 
    for index, file_path in enumerate (matching_filenames): 
     upload_file (file_path, index + 1, total_count) 

def run_upload (options, paths): 
    upload_file = make_upload_file (**options) 

    for arg in paths: 
     path = abspath (arg) 
     if isdir (path): 
      upload_directory (path, upload_file) 
     elif isfile (path): 
      upload_file (path) 
     else: 
      logging.error ('No such path: %r' % path) 

    logging.info ('Done!') 
+1

On python 2.6.6 tôi đã nhận được một lỗi trong ranh giới phân tích dung nhiều khi sử dụng mã này trên Windows. Tôi đã phải thay đổi từ string.letters thành string.ascii_letters như được thảo luận tại http://stackoverflow.com/questions/2823316/generate-a-random-letter-in-python/2823331#2823331 để làm việc này. Yêu cầu về ranh giới được thảo luận tại đây: http://stackoverflow.com/questions/147451/what-are-valid-characters-for-creating-a-multipart-form-boundary/147467#147467 –

+0

gọi run_upload ({' server ':' ',' thread ':' '}, paths = ['/path/to/file.txt ']) gây ra lỗi trong dòng này: upload_file (đường dẫn) vì "tệp tải lên" yêu cầu 3 tham số vì vậy tôi thay thế bằng dòng này upload_file (đường dẫn, 1, 1) – Radian

0

Bạn cũng có thể muốn có một cái nhìn tại httplib2, với examples. Tôi tìm thấy bằng cách sử dụng httplib2 là ngắn gọn hơn so với việc sử dụng các mô-đun HTTP tích hợp.

+1

Không có ví dụ nào cho thấy cách xử lý các tệp tải lên. – dland

+0

Liên kết đã lỗi thời + không có ví dụ được nêu. – jlr

+1

Từ đó đã chuyển đến https://github.com/httplib2/httplib2. Mặt khác, ngày nay tôi có lẽ sẽ đề nghị 'yêu cầu' thay thế. – pdc

2

Thư viện của Chris Atlee poster hoạt động thực sự tốt cho điều này (đặc biệt là chức năng tiện lợi poster.encode.multipart_encode()). Là một phần thưởng, nó hỗ trợ phát trực tuyến các tệp lớn mà không cần tải toàn bộ tệp vào bộ nhớ. Xem thêm Python issue 3244.

4

Điều duy nhất ngăn bạn sử dụng urlopen trực tiếp trên đối tượng tệp là thực tế là đối tượng tệp được dựng sẵn thiếu định nghĩa len. Một cách đơn giản là tạo một lớp con, cung cấp urlopen với tệp chính xác. Tôi cũng đã sửa đổi tiêu đề Loại Nội dung trong tệp bên dưới.

import os 
import urllib2 
class EnhancedFile(file): 
    def __init__(self, *args, **keyws): 
     file.__init__(self, *args, **keyws) 

    def __len__(self): 
     return int(os.fstat(self.fileno())[6]) 

theFile = EnhancedFile('a.xml', 'r') 
theUrl = "http://example.com/abcde" 
theHeaders= {'Content-Type': 'text/xml'} 

theRequest = urllib2.Request(theUrl, theFile, theHeaders) 

response = urllib2.urlopen(theRequest) 

theFile.close() 


for line in response: 
    print line 
+0

@robert Tôi kiểm tra mã của bạn bằng Python2.7 nhưng nó không hoạt động. urlopen (Yêu cầu (theUrl, theFile, ...)) chỉ mã hóa nội dung của tệp như một bài đăng bình thường nhưng không thể chỉ định trường biểu mẫu chính xác. Tôi thậm chí thử biến thể urlopen (theUrl, urlencode ({'serverside_field_name': EnhancedFile ('my_file.txt')})), nó tải lên một tệp nhưng (tất nhiên!) Với nội dung không đúng như . Tôi đã bỏ lỡ điều gì đó? – RayLuo

+0

Cảm ơn câu trả lời. Bằng cách sử dụng đoạn mã trên, tôi đã chuyển tập tin hình ảnh thô 2,2 GB bằng cách sử dụng yêu cầu PUT vào máy chủ web. –

157

Từ http://docs.python-requests.org/en/latest/user/quickstart/#post-a-multipart-encoded-file

Các yêu cầu làm cho nó rất đơn giản để upload file dung nhiều mã hóa:

>>> with open('report.xls', 'rb') as f: r = requests.post('http://httpbin.org/post', files={'report.xls': f}) 

Vậy là xong. Tôi không nói đùa - đây là một dòng mã. Tệp đã được gửi. Hãy kiểm tra:

>>> r.text 
{ 
    "origin": "179.13.100.4", 
    "files": { 
    "report.xls": "<censored...binary...data>" 
    }, 
    "form": {}, 
    "url": "http://httpbin.org/post", 
    "args": {}, 
    "headers": { 
    "Content-Length": "3196", 
    "Accept-Encoding": "identity, deflate, compress, gzip", 
    "Accept": "*/*", 
    "User-Agent": "python-requests/0.8.0", 
    "Host": "httpbin.org:80", 
    "Content-Type": "multipart/form-data; boundary=127.0.0.1.502.21746.1321131593.786.1" 
    }, 
    "data": "" 
} 
+1

Tôi đang thử điều tương tự và hoạt động tốt nếu kích thước tệp nhỏ hơn ~ 1,5 MB. khác ném một lỗi của nó .. xin vui lòng xem [ở đây] (http://stackoverflow.com/questions/20217348/requests-post-files-upload-large-file-more-than-1-5-mb-python). –

+1

những gì cố gắng làm là đăng nhập vào một số trang web bằng cách yêu cầu mà tôi đã thực hiện thành công nhưng bây giờ tôi muốn tải lên video sau khi đăng nhập và biểu mẫu có các trường khác nhau sẽ được điền trước khi gửi. Vì vậy, làm thế nào tôi nên vượt qua những giá trị như video mô tả, tiêu đề video vv – TaraGurung

+14

Bạn có thể muốn làm 'với mở ('report.xls', 'rb') là f: r = requests.post ('http: // Thay vào đó, httpbin.org/post ', files = {' report.xls ': f}) 'sẽ đóng lại tệp sau khi mở. – Hjulle

3

Có vẻ như yêu cầu python không xử lý các tệp nhiều phần cực lớn.

Tài liệu đề xuất bạn xem xét requests-toolbelt.

Here's the pertinent page từ tài liệu của họ.

0
def visit_v2(device_code, camera_code): 
    image1 = MultipartParam.from_file("files", "/home/yuzx/1.txt") 
    image2 = MultipartParam.from_file("files", "/home/yuzx/2.txt") 
    datagen, headers = multipart_encode([('device_code', device_code), ('position', 3), ('person_data', person_data), image1, image2]) 
    print "".join(datagen) 
    if server_port == 80: 
     port_str = "" 
    else: 
     port_str = ":%s" % (server_port,) 
    url_str = "http://" + server_ip + port_str + "/adopen/device/visit_v2" 
    headers['nothing'] = 'nothing' 
    request = urllib2.Request(url_str, datagen, headers) 
    try: 
     response = urllib2.urlopen(request) 
     resp = response.read() 
     print "http_status =", response.code 
     result = json.loads(resp) 
     print resp 
     return result 
    except urllib2.HTTPError, e: 
     print "http_status =", e.code 
     print e.read() 
2

Tôi cố gắng để kiểm tra django api nghỉ ngơi và làm việc của nó cho tôi:

def test_upload_file(self): 
     filename = "/Users/Ranvijay/tests/test_price_matrix.csv" 
     data = {'file': open(filename, 'rb')} 
     client = APIClient() 
     # client.credentials(HTTP_AUTHORIZATION='Token ' + token.key) 
     response = client.post(reverse('price-matrix-csv'), data, format='multipart') 

     print response 
     self.assertEqual(response.status_code, status.HTTP_200_OK) 
Các vấn đề liên quan