python ร้องขอการอัปโหลดไฟล์


125

ฉันทำงานง่ายๆในการอัปโหลดไฟล์โดยใช้ไลบรารีคำขอของ Python ฉันค้นหา Stack Overflow และดูเหมือนจะไม่มีใครมีปัญหาเดียวกันนั่นคือเซิร์ฟเวอร์ไม่ได้รับไฟล์:

import requests
url='http://nesssi.cacr.caltech.edu/cgi-bin/getmulticonedb_release2.cgi/post'
files={'files': open('file.txt','rb')}
values={'upload_file' : 'file.txt' , 'DB':'photcat' , 'OUT':'csv' , 'SHORT':'short'}
r=requests.post(url,files=files,data=values)

ฉันกรอกค่าของคีย์เวิร์ด 'upload_file' ด้วยชื่อไฟล์ของฉันเพราะถ้าฉันปล่อยว่างไว้มันจะขึ้นว่า

Error - You must select a file to upload!

และตอนนี้ฉันได้รับ

File  file.txt  of size    bytes is  uploaded successfully!
Query service results:  There were 0 lines.

ซึ่งจะปรากฏขึ้นเมื่อไฟล์ว่างเปล่าเท่านั้น ฉันติดอยู่กับวิธีการส่งไฟล์ให้สำเร็จ ฉันรู้ว่าไฟล์ใช้งานได้เพราะถ้าฉันไปที่เว็บไซต์นี้และกรอกแบบฟอร์มด้วยตนเองมันจะส่งคืนรายการวัตถุที่ตรงกันซึ่งเป็นสิ่งที่ฉันตามมา ฉันขอขอบคุณทุกคำแนะนำจริงๆ

หัวข้ออื่น ๆ ที่เกี่ยวข้อง (แต่ไม่ตอบปัญหาของฉัน):

คำตอบ:


211

หากupload_fileหมายถึงไฟล์ให้ใช้:

files = {'upload_file': open('file.txt','rb')}
values = {'DB': 'photcat', 'OUT': 'csv', 'SHORT': 'short'}

r = requests.post(url, files=files, data=values)

และrequestsจะส่งเนื้อหา POST ในรูปแบบหลายส่วนพร้อมupload_fileฟิลด์ที่ตั้งค่าเป็นเนื้อหาของfile.txtไฟล์

ชื่อไฟล์จะรวมอยู่ในส่วนหัวละครใบ้สำหรับฟิลด์เฉพาะ:

>>> import requests
>>> open('file.txt', 'wb')  # create an empty demo file
<_io.BufferedWriter name='file.txt'>
>>> files = {'upload_file': open('file.txt', 'rb')}
>>> print(requests.Request('POST', 'http://example.com', files=files).prepare().body.decode('ascii'))
--c226ce13d09842658ffbd31e0563c6bd
Content-Disposition: form-data; name="upload_file"; filename="file.txt"


--c226ce13d09842658ffbd31e0563c6bd--

สังเกตfilename="file.txt"พารามิเตอร์

คุณสามารถใช้ทูเพิลสำหรับfilesค่าการแมปโดยมีองค์ประกอบระหว่าง 2 ถึง 4 องค์ประกอบหากคุณต้องการการควบคุมเพิ่มเติม องค์ประกอบแรกคือชื่อไฟล์ตามด้วยเนื้อหาและค่าส่วนหัวชนิดเนื้อหาที่เป็นทางเลือกและการแมปส่วนหัวเพิ่มเติมที่เป็นทางเลือก:

files = {'upload_file': ('foobar.txt', open('file.txt','rb'), 'text/x-spam')}

สิ่งนี้จะตั้งชื่อไฟล์และประเภทเนื้อหาทางเลือกโดยเว้นส่วนหัวที่เป็นทางเลือก

หากคุณหมายถึงเนื้อหา POST ทั้งหมดที่จะนำมาจากไฟล์ (โดยไม่ได้ระบุฟิลด์อื่นไว้) อย่าใช้filesพารามิเตอร์เพียงแค่โพสต์ไฟล์โดยตรงในรูปแบบdata. จากนั้นคุณอาจต้องการตั้งค่าContent-Typeส่วนหัวด้วยเนื่องจากจะไม่มีการตั้งค่าเป็นอย่างอื่น ดูคำขอหลาม - ข้อมูล POST จากแฟ้ม


สวัสดีฉันจะส่งไฟล์หลายไฟล์โดยใช้ชื่อเดียวกันได้อย่างไร เช่น 'ไฟล์แนบ' เป็นต้น
William Wino

4
@ วิลเลียม: คุณสามารถใช้ลำดับของทูเปิล 2 ค่าได้เช่นกันซึ่งช่วยให้คุณใช้ชื่อฟิลด์files = [('attachment', open('attachment1.txt', 'rb')), ('attachment', open('attachment2.txt', 'rb'))]ซ้ำได้: ทูเปิลแต่ละตัวเป็นคู่ของคีย์และค่า
Martijn Pieters

2
นอกจากนี้คุณยังสามารถใช้ได้files={'file':('nameoffile',open('namoffile','rb'),'Content-Type':'text/html','other header'),'file2':('nameoffile2',open('nameoffile2','rb'),'Content-Type':'application/xml','other header')}แต่ถ้าใช้ไฟล์ = {} แล้วจะต้องไม่ใช้ headers = {'Content-Type': 'blah blah'}! -> @ martijn-pieters: เนื่องจากประเภทเนื้อหาหลายส่วน / แบบฟอร์มข้อมูลต้องมีค่าขอบเขตที่ใช้ในการแบ่งส่วนต่างๆในเนื้อหาของโพสต์ การไม่ตั้งค่าส่วนหัว Content-Type ทำให้แน่ใจว่าคำร้องขอตั้งค่าเป็นค่าที่ถูกต้อง
zaki

1
@MartijnPieters นี้ไม่เสี่ยงต่อการรั่วไหลของไฟล์หรือไม่? ไม่requestsปิด?
Matt Messersmith

4
@MattMessersmith: ไม่มันไม่ได้ปิด หากคุณต้องการปิดไฟล์ให้ใช้with open(...) as fobj:และใช้fobjในการfilesแมป
Martijn Pieters

36

(2018) ไลบรารีคำขอ python ใหม่ทำให้กระบวนการนี้ง่ายขึ้นเราสามารถใช้ตัวแปร 'files' เพื่อส่งสัญญาณว่าเราต้องการอัปโหลดไฟล์ที่เข้ารหัสหลายส่วน

url = 'http://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}

r = requests.post(url, files=files)
r.text

3
การร้องขอไลบรารีปิดไฟล์โดยอัตโนมัติหรือไม่
Demetris

1
สวัสดีฉันใช้ห้องสมุดนี้มาสักพักแล้ว เป็นคำถามที่ดี คุณช่วยฉันและคนอื่น ๆ ด้วยการพิมพ์ lsof | grep "filename" และแบ่งปันผลลัพธ์ของคุณกับเรา? ขอบคุณ :)
laycat

1
ด้วยการใช้lsofดูเหมือนว่าไฟล์ยังคงเปิดอยู่หรืออย่างน้อยก็เป็นวิธีที่ฉันตีความผลลัพธ์ต่อไปนี้ ก่อนหน้านี้การเรียกใช้openไม่มีระเบียนในlsofตารางเกี่ยวกับfilename. จากนั้นหลังจากopenดำเนินการแล้วหลายระเบียนจะปรากฏขึ้นพร้อมreadการเข้าถึง หลังจากดำเนินการrequests.postแล้วบันทึกยังคงอยู่ที่นั่นแสดงว่าไฟล์ไม่ได้ปิด
Demetris

23

อัปโหลดลูกค้า

หากคุณต้องการอัปโหลดไฟล์เดียวกับงูหลามrequestsห้องสมุดแล้วขอ lib สนับสนุนสตรีมมิ่งไฟล์อัพโหลดซึ่งช่วยให้คุณสามารถส่งไฟล์ขนาดใหญ่หรือลำธารโดยไม่ต้องอ่านในหน่วยความจำ

with open('massive-body', 'rb') as f:
    requests.post('http://some.url/streamed', data=f)

ฝั่งเซิร์ฟเวอร์

จากนั้นเก็บไฟล์ไว้server.pyด้านข้างเพื่อบันทึกสตรีมลงในไฟล์โดยไม่ต้องโหลดลงในหน่วยความจำ ต่อไปนี้เป็นตัวอย่างที่มีการใช้ขวดอัพโหลดไฟล์

@app.route("/upload", methods=['POST'])
def upload_file():
    from werkzeug.datastructures import FileStorage
    FileStorage(request.stream).save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
    return 'OK', 200

หรือใช้การแยกวิเคราะห์ข้อมูลแบบฟอร์ม werkzeugตามที่กล่าวไว้ในการแก้ไขปัญหา "การอัปโหลดไฟล์ขนาดใหญ่กินหน่วยความจำ " เพื่อหลีกเลี่ยงการใช้หน่วยความจำอย่างไม่มีประสิทธิภาพในการอัปโหลดไฟล์ขนาดใหญ่ (ไฟล์ st 22 GiB ใน ~ 60 วินาทีการใช้หน่วยความจำจะคงที่ที่ประมาณ 13 MiB.).

@app.route("/upload", methods=['POST'])
def upload_file():
    def custom_stream_factory(total_content_length, filename, content_type, content_length=None):
        import tempfile
        tmpfile = tempfile.NamedTemporaryFile('wb+', prefix='flaskapp', suffix='.nc')
        app.logger.info("start receiving file ... filename => " + str(tmpfile.name))
        return tmpfile

    import werkzeug, flask
    stream, form, files = werkzeug.formparser.parse_form_data(flask.request.environ, stream_factory=custom_stream_factory)
    for fil in files.values():
        app.logger.info(" ".join(["saved form name", fil.name, "submitted as", fil.filename, "to temporary file", fil.stream.name]))
        # Do whatever with stored file at `fil.stream.name`
    return 'OK', 200

0

ใน Ubuntu คุณสามารถใช้วิธีนี้

เพื่อบันทึกไฟล์ในบางตำแหน่ง (ชั่วคราว) จากนั้นเปิดและส่งไปยัง API

      path = default_storage.save('static/tmp/' + f1.name, ContentFile(f1.read()))
      path12 = os.path.join(os.getcwd(), "static/tmp/" + f1.name)
      data={} #can be anything u want to pass along with File
      file1 = open(path12, 'rb')
      header = {"Content-Disposition": "attachment; filename=" + f1.name, "Authorization": "JWT " + token}
       res= requests.post(url,data,header)

ค่าdataตัวแปรคืออะไร?
am.rez

อาจเป็นอะไรก็ได้เช่นชื่อผู้ใช้ฉันเพิ่งแสดงวิธีอัปโหลดไฟล์ไปยัง REST apis
Harshit Trivedi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.