Python requests_toolbelt MultipartEncoder 文件名
Python requests_toolbelt MultipartEncoder filename
使用requests_toolbelt以Multipart形式上传大文件,我在下面构建了一个成功上传文件的方法,但是我无法访问发布的文件名。如何访问服务器上的文件名?
# client-side
file = open('/Volumes/Extra/test/my_video.mpg', 'rb')
payload = MultipartEncoder({file.name: file})
r = requests.post(url, data=payload, headers={'Content-Type': 'application/octet-stream'})
# server-side
@view_config(route_name='remote.agent_upload', renderer='json')
def remote_agent_upload(request):
r = request.response
fs = request.body_file
f = open('/Volumes/Extra/tests2/bar.mpg', 'wb') # wish to use filename here
f.write(fs.read())
fs.close()
f.close()
return r
好的,看起来您正在使用文件名作为字段名。此外,您这样做的方式似乎整个 post 内容都被写入文件...这是期望的结果吗?您是否尝试过在服务器端写入 mpg 文件后实际播放它们?
我目前没有可用于测试的 HTTP 服务器,它会自动给我一个请求对象,但我假设请求对象是一个 webob.Request 对象(至少看起来像原来如此,如有不妥请指正)
好的,让我给你看我的测试。 (这适用于 python3.4,不确定你使用的是什么版本的 Python,但我认为它也应该适用于 Python 2.7 - 尽管未测试)
这个测试中的代码有点长,但是注释很多,可以帮助你理解我每一步都做了什么。希望它能让您更好地了解 HTTP 请求和响应在 python 中如何使用您正在使用的工具
# My Imports
from requests_toolbelt import MultipartEncoder
from webob import Request
import io
# Create a buffer object that can be read by the MultipartEncoder class
# This works just like an open file object
file = io.BytesIO()
# The file content will be simple for my test.
# But you could just as easily have a multi-megabyte mpg file
# Write the contents to the file
file.write(b'test mpg content')
# Then seek to the beginning of the file so that the
# MultipartEncoder can read it from the beginning
file.seek(0)
# Create the payload
payload = MultipartEncoder(
{
# The name of the file upload field... Not the file name
'uploadedFile': (
# This would be the name of the file
'This is my file.mpg',
# The file handle that is ready to be read from
file,
# The content type of the file
'application/octet-stream'
)
}
)
# To send the file, you would use the requests.post method
# But the content type is not application-octet-stream
# The content type is multipart/form-data; with a boundary string
# Without the proper header type, your server would not be able to
# figure out where the file begins and ends and would think the
# entire post content is the file, which it is not. The post content
# might even contain multiple files
# So, to send your file, you would use:
#
# response = requests.post(url, data=payload, headers={'Content-Type': payload.content_type})
# Instead of sending the payload to the server,
# I am just going to grab the output as it would be sent
# This is because I don't have a server, but I can easily
# re-create the object using this output
postData = payload.to_string()
# Create an input buffer object
# This will be read by our server (our webob.Request object)
inputBuffer = io.BytesIO()
# Write the post data to the input buffer so that the webob.Request object can read it
inputBuffer.write(postData)
# And, once again, seek to 0
inputBuffer.seek(0)
# Create an error buffer so that errors can be written to it if there are any
errorBuffer = io.BytesIO()
# Setup our wsgi environment just like the server would give us
environment = {
'HTTP_HOST': 'localhost:80',
'PATH_INFO': '/index.py',
'QUERY_STRING': '',
'REQUEST_METHOD': 'POST',
'SCRIPT_NAME': '',
'SERVER_NAME': 'localhost',
'SERVER_PORT': '80',
'SERVER_PROTOCOL': 'HTTP/1.0',
'CONTENT_TYPE': payload.content_type,
'wsgi.errors': errorBuffer,
'wsgi.input': inputBuffer,
'wsgi.multiprocess': False,
'wsgi.multithread': False,
'wsgi.run_once': False,
'wsgi.url_scheme': 'http',
'wsgi.version': (1, 0)
}
# Create our request object
# This is the same as your request object and should have all our info for reading
# the file content as well as the file name
request = Request(environment)
# At this point, the request object is the same as what you get on your server
# So, from this point on, you can use the following code to get
# your actual file content as well as your file name from the object
# Our uploaded file is in the POST. And the POST field name is 'uploadedFile'
# Grab our file so that it can be read
uploadedFile = request.POST['uploadedFile']
# To read our content, you can use uploadedFile.file.read()
print(uploadedFile.file.read())
# And to get the file name, you can use uploadedFile.filename
print(uploadedFile.filename)
所以,我认为这个修改后的代码对你有用。 (希望)
同样,没有测试,因为我实际上没有可以测试的服务器。而且,我不知道你的 "request" 对象在服务器端是什么类型的对象......好的,这里是:
# client-side
import requests
file = open('/Volumes/Extra/test/my_video.mpg', 'rb')
payload = MultipartEncoder({'uploadedFile': (file.name, file, 'application/octet-stream')})
r = requests.post('http://somewhere/somefile.py', data=payload, headers={'Content-Type': payload.content_type})
# server-side
@view_config(route_name='remote.agent_upload', renderer='json')
def remote_agent_upload(request):
# Write your actual file contents, not the post data which contains multi part boundary
uploadedFile = request.POST['uploadedFile']
fs = uploadedFile.file
# The file name is insecure. What if the file name comes through as '../../../etc/passwd'
# If you don't secure this, you've just wiped your /etc/passwd file and your server is toast
# (assuming the web user has write permission to the /etc/passwd file
# which it shouldn't, but just giving you a worst case scenario)
fileName = uploadedFile.filename
# Secure the fileName here...
# Make sure it doesn't have any slashes or double dots, or illegal characters, etc.
# I'll leave that up to you
# Write the file
f = open('/Volumes/Extra/tests2/' + fileName, 'wb')
f.write(fs.read())
对于原始 OP 来说可能为时已晚,但可能会帮助其他人。这就是我使用 MultipartEncoder 在 multipart/form-data 上传中上传带有 json 的文件的方式。当我需要一个文件作为多部分请求的一部分以单个 json 字符串作为二进制文件上传时(因此只有两部分,文件和 json)。请注意,在创建我的请求 header(这是接收服务器指定的自定义 header)时,我从编码的 object 中获得 content_type(它通常作为 multipart/form-data).我正在使用 simplejson.dumps,但我相信你可以只使用 json.dumps。
m = MultipartEncoder([
('json', (None, simplejson.dumps(datapayload), 'text/plain')),
('file', (os.path.basename(file_path), open(file_path, 'rb'), 'text/plain'))],
None, encoding='utf-8')
headers = {'Authorization': 'JwToken' + ' ' + jwt_str, 'content-type': m.content_type}
response = requests.post(uri, headers=headers, data=m, timeout=45, verify=True )
在文件部分,该字段称为 "file",但我使用 os.path.basename(file_path)
从完整文件路径中获取文件名,例如c:\temp\mytestfile.txt 。如果我愿意,我可以在该字段中轻松地将该文件称为其他名称(这不是原始名称)。
使用requests_toolbelt以Multipart形式上传大文件,我在下面构建了一个成功上传文件的方法,但是我无法访问发布的文件名。如何访问服务器上的文件名?
# client-side
file = open('/Volumes/Extra/test/my_video.mpg', 'rb')
payload = MultipartEncoder({file.name: file})
r = requests.post(url, data=payload, headers={'Content-Type': 'application/octet-stream'})
# server-side
@view_config(route_name='remote.agent_upload', renderer='json')
def remote_agent_upload(request):
r = request.response
fs = request.body_file
f = open('/Volumes/Extra/tests2/bar.mpg', 'wb') # wish to use filename here
f.write(fs.read())
fs.close()
f.close()
return r
好的,看起来您正在使用文件名作为字段名。此外,您这样做的方式似乎整个 post 内容都被写入文件...这是期望的结果吗?您是否尝试过在服务器端写入 mpg 文件后实际播放它们?
我目前没有可用于测试的 HTTP 服务器,它会自动给我一个请求对象,但我假设请求对象是一个 webob.Request 对象(至少看起来像原来如此,如有不妥请指正)
好的,让我给你看我的测试。 (这适用于 python3.4,不确定你使用的是什么版本的 Python,但我认为它也应该适用于 Python 2.7 - 尽管未测试)
这个测试中的代码有点长,但是注释很多,可以帮助你理解我每一步都做了什么。希望它能让您更好地了解 HTTP 请求和响应在 python 中如何使用您正在使用的工具
# My Imports
from requests_toolbelt import MultipartEncoder
from webob import Request
import io
# Create a buffer object that can be read by the MultipartEncoder class
# This works just like an open file object
file = io.BytesIO()
# The file content will be simple for my test.
# But you could just as easily have a multi-megabyte mpg file
# Write the contents to the file
file.write(b'test mpg content')
# Then seek to the beginning of the file so that the
# MultipartEncoder can read it from the beginning
file.seek(0)
# Create the payload
payload = MultipartEncoder(
{
# The name of the file upload field... Not the file name
'uploadedFile': (
# This would be the name of the file
'This is my file.mpg',
# The file handle that is ready to be read from
file,
# The content type of the file
'application/octet-stream'
)
}
)
# To send the file, you would use the requests.post method
# But the content type is not application-octet-stream
# The content type is multipart/form-data; with a boundary string
# Without the proper header type, your server would not be able to
# figure out where the file begins and ends and would think the
# entire post content is the file, which it is not. The post content
# might even contain multiple files
# So, to send your file, you would use:
#
# response = requests.post(url, data=payload, headers={'Content-Type': payload.content_type})
# Instead of sending the payload to the server,
# I am just going to grab the output as it would be sent
# This is because I don't have a server, but I can easily
# re-create the object using this output
postData = payload.to_string()
# Create an input buffer object
# This will be read by our server (our webob.Request object)
inputBuffer = io.BytesIO()
# Write the post data to the input buffer so that the webob.Request object can read it
inputBuffer.write(postData)
# And, once again, seek to 0
inputBuffer.seek(0)
# Create an error buffer so that errors can be written to it if there are any
errorBuffer = io.BytesIO()
# Setup our wsgi environment just like the server would give us
environment = {
'HTTP_HOST': 'localhost:80',
'PATH_INFO': '/index.py',
'QUERY_STRING': '',
'REQUEST_METHOD': 'POST',
'SCRIPT_NAME': '',
'SERVER_NAME': 'localhost',
'SERVER_PORT': '80',
'SERVER_PROTOCOL': 'HTTP/1.0',
'CONTENT_TYPE': payload.content_type,
'wsgi.errors': errorBuffer,
'wsgi.input': inputBuffer,
'wsgi.multiprocess': False,
'wsgi.multithread': False,
'wsgi.run_once': False,
'wsgi.url_scheme': 'http',
'wsgi.version': (1, 0)
}
# Create our request object
# This is the same as your request object and should have all our info for reading
# the file content as well as the file name
request = Request(environment)
# At this point, the request object is the same as what you get on your server
# So, from this point on, you can use the following code to get
# your actual file content as well as your file name from the object
# Our uploaded file is in the POST. And the POST field name is 'uploadedFile'
# Grab our file so that it can be read
uploadedFile = request.POST['uploadedFile']
# To read our content, you can use uploadedFile.file.read()
print(uploadedFile.file.read())
# And to get the file name, you can use uploadedFile.filename
print(uploadedFile.filename)
所以,我认为这个修改后的代码对你有用。 (希望) 同样,没有测试,因为我实际上没有可以测试的服务器。而且,我不知道你的 "request" 对象在服务器端是什么类型的对象......好的,这里是:
# client-side
import requests
file = open('/Volumes/Extra/test/my_video.mpg', 'rb')
payload = MultipartEncoder({'uploadedFile': (file.name, file, 'application/octet-stream')})
r = requests.post('http://somewhere/somefile.py', data=payload, headers={'Content-Type': payload.content_type})
# server-side
@view_config(route_name='remote.agent_upload', renderer='json')
def remote_agent_upload(request):
# Write your actual file contents, not the post data which contains multi part boundary
uploadedFile = request.POST['uploadedFile']
fs = uploadedFile.file
# The file name is insecure. What if the file name comes through as '../../../etc/passwd'
# If you don't secure this, you've just wiped your /etc/passwd file and your server is toast
# (assuming the web user has write permission to the /etc/passwd file
# which it shouldn't, but just giving you a worst case scenario)
fileName = uploadedFile.filename
# Secure the fileName here...
# Make sure it doesn't have any slashes or double dots, or illegal characters, etc.
# I'll leave that up to you
# Write the file
f = open('/Volumes/Extra/tests2/' + fileName, 'wb')
f.write(fs.read())
对于原始 OP 来说可能为时已晚,但可能会帮助其他人。这就是我使用 MultipartEncoder 在 multipart/form-data 上传中上传带有 json 的文件的方式。当我需要一个文件作为多部分请求的一部分以单个 json 字符串作为二进制文件上传时(因此只有两部分,文件和 json)。请注意,在创建我的请求 header(这是接收服务器指定的自定义 header)时,我从编码的 object 中获得 content_type(它通常作为 multipart/form-data).我正在使用 simplejson.dumps,但我相信你可以只使用 json.dumps。
m = MultipartEncoder([
('json', (None, simplejson.dumps(datapayload), 'text/plain')),
('file', (os.path.basename(file_path), open(file_path, 'rb'), 'text/plain'))],
None, encoding='utf-8')
headers = {'Authorization': 'JwToken' + ' ' + jwt_str, 'content-type': m.content_type}
response = requests.post(uri, headers=headers, data=m, timeout=45, verify=True )
在文件部分,该字段称为 "file",但我使用 os.path.basename(file_path)
从完整文件路径中获取文件名,例如c:\temp\mytestfile.txt 。如果我愿意,我可以在该字段中轻松地将该文件称为其他名称(这不是原始名称)。