I'm trying to upload a bulk data exchange call to eBay using their FileTransfer API.
In order to do this, I've got to POST an xml request string to the eBay server but the xml
request contains a <Data>
section that must include the data you're transferring( in this case it's a base64 encoded zipfile containing another xml document). eBay gives an example in C# of how to construct such a document: https://ebay.custhelp.com/app/answers/detail/a_id/1561
I've been trying to recreate this example in python using httplib to POST a string I've constructed in much the same fashion as the example(the 3 UUIDs are unique):
request = """
--MIMEBoundaryurn_uuid_{XMLUUID}
Content-Type: application/xop+xml;charset=UTF-8;type="text/xml;charset=UTF-8";
Content-Transfer-Encoding: binary
Content-ID:<0.urn:uuid:{REQUUID}>
<?xml version="1.0" encoding="utf-8"?>
<uploadFileRequest xmlns:sct=\"http://www.ebay.com/soaframework/common/types\" xmlns="http://www.ebay.com/marketplace/services">
<fileAttachment>
<Size>{Size}</Size>
<Data><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include"
href="cid:urn:uuid:{ATTCHMNTUUID}>"</Data>
</fileAttachment>
<fileFormat>{fileFormat}</fileFormat>
<fileReferenceId>{fileReferenceId}</fileReferenceId>
<taskReferenceId>{taskReferenceId}</taskReferenceId>
</uploadFileRequest>
--MIMEBoundaryurn_uuid_{XMLUUID}
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Content-ID: <urn.uuid:{ATTCHMNTUUID}>\r\n
{Data}
--MIMEBoundaryurn_uuid_{XMLUUID}--
""".replace("\t", "")
request_dict = {
'Size': size,
'Data': payload,
'fileFormat': 'zip',
'fileReferenceId': '50000935383',
'taskReferenceId': '50000847753',
'REQUUID': reqUUID,
'XMLUUID': xmlUUID,
'ATTCHMNTUUID': attchmntUUID,
}
request = request.format( **request_dict )
With headers that look like this:
headers = {
'X-EBAY-SOA-OPERATION-NAME': 'uploadFile',
'X-EBAY-SOA-SERVICE-NAME': 'FileTransferService',
'X-EBAY-SOA-SECURITY-TOKEN': #Auth Token,
'Content-type': "multipart/related; boundary=" + boundary + ";type=\"application/xop+xml\";start=\"<0." + "urn:uuid:" + str(requuid) + ">\";start-info=\"text/xml\""
}
and then my POST:
connection = httplib.HTTPSConnection( 'storage.sandbox.ebay.com' )
connection.request( "POST", '/FileTransferService', request, headers )
When I post the xml request without the MIME attachment information it accepts the file with no problems. But when I try to do it like the above code, with MIME multipart/related attachments and the <xop>
tag pointing out where the data is located in the attachment, the POST isn't successful and I get a "Error 302: Moved Temporarily" response. This leads me to believe that something isn't right with how I constructed the MIME multipart/related xml request, or how I constructed the "Content-type" header declaration, or my use of xop, or most likely a combination of all three things.
I guess my question is: How do I create an xml request that contains MIME multipart/related sections and also uses xop?
Thanks for the help!
Wes
I figured out what was wrong with my request. It's a combination of constructing the request string and also the headers. I went with a much more organized and programmatic approach to it, and it worked.
Here's what the section that builds the request string looks like:
###########################################
# UUIDs
###########################################
reqUUID= uuid.uuid4()
attchmntUUID = uuid.uuid4()
##########################################
# MIME strings
##########################################
URN_UUID_REQUEST = "<0.urn:uuid:%s>"% reqUUID
URN_UUID_ATTACHMENT = "urn:uuid:%s" % attchmntUUID
MIME_BOUNDARY = "MIME_boundary"
request_dict = {
'Size': size,
'Data': payload,
'fileFormat': 'gzip',
'fileReferenceId': '50000945773',
'taskReferenceId': '50000858033',
'REQUUID': reqUUID,
'ATTCHMNTUUID': attchmntUUID,
}
def build_request( request_dict):
'''
Build the request string with MIME Attachment
'''
request = '<uploadFileRequest xmlns:sct="http://www.ebay.com/soaframework/common/types" xmlns="http://www.ebay.com/marketplace/services">\r\n'
request += '<taskReferenceId>%s</taskReferenceId>\r\n' % request_dict['taskReferenceId']
request += '<fileReferenceId>%s</fileReferenceId>\r\n' % request_dict['fileReferenceId']
request += '<fileFormat>%s</fileFormat>\r\n' % request_dict['fileFormat']
request += '<fileAttachment>\r\n'
request += '<Size>%s</Size>\r\n'% request_dict['Size']
request += '<Data><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:%s"/></Data>\r\n'%URN_UUID_ATTACHMENT
request += '</fileAttachment>\r\n'
request += '</uploadFileRequest>\r\n'
return request
def build_mime_message( request, data ):
'''
Build the xml string with MIME attachments and the base64 encoded data string
'''
request_part = '\r\n'
request_part += '--%s\r\n' % MIME_BOUNDARY
request_part += 'Content-Type: application/xop+xml; charset=UTF-8; type="text/xml; charset=UTF-8"\r\n'
request_part += 'Content-Transfer_Encoding: binary\r\n'
request_part += 'Content-ID: %s\r\n\r\n' % URN_UUID_REQUEST
request_part += '%s\r\n' % request
binary_part = '\r\n'
binary_part += '--%s\r\n' % MIME_BOUNDARY
binary_part += 'Content-Type: application/octet-stream\r\n'
binary_part += 'Content-Transfer-Encoding: base64\r\n'
binary_part += 'Content-ID: <%s>\r\n\r\n' % URN_UUID_ATTACHMENT
binary_part += '%s\r\n' % data
binary_part += '--%s--' % MIME_BOUNDARY
return request_part + binary_part
request = build_request( request_dict )
request = build_mime_message( request, data )#data is base64 encoded gzip compressed xml file
And the headers look like this:
content_type_string = 'multipart/related;'
content_type_string += ' boundary=%s;' % MIME_BOUNDARY
content_type_string += ' type="application/xop+xml";'
content_type_string += ' start="%s";' % URN_UUID_REQUEST
content_type_string += ' start-info="text/xml"'
headers = {
'X-EBAY-SOA-OPERATION-NAME': 'uploadFile',
'X-EBAY-SOA-SERVICE-NAME': 'FileTransferService',
'X-EBAY-SOA-SECURITY-TOKEN': #auth token,
'Content-Length': len( request ),
'Content-Type': content_type_string
}
So from all of this, I gathered that the problem was with the newlines and the tab characters in both the header and request.