Uploading Office Open XML files using API POST method in Python - python-3.x

I am trying to write a script to help me automate some work with our CAT tool (Memsource). To this end, I need to upload some files using API.
I rely on Memsource API documentation available here: https://cloud.memsource.com/web/docs/api#operation/createJob
I wrote a short code to test file uploading before moving to making it async, and I have some serious problem: text files are uploaded correctly, although the body of the text contains some additions after uploading:
--4002a5507da490554ad71ce8591ccf69
Content-Disposition: form-data; name="file"; filename=“test.txt"
I also tried to upload DOCX file, but it cannot be even opened in Memsource online editor — I guess the content is modified along the way, but I am unable to find where...
The code responsible for the upload is as follows:
def test_upload(self):
# Assemble "Memsource" header as mentioned in the API docs
Memsource_header = {
"targetLangs": ["pl"],
}
# Open the file to be uploaded and extract file name
f = open("Own/TMS_CAT/test.txt", "rb")
f_name = os.path.basename(f.name)
# Assemble the request header
header = {
"Memsource": json.dumps(Memsource_header),
"Content-Disposition": f'attachment; filename="{f_name}"',
"Authorization": f"ApiToken {self.authToken}",
"Content-Type": "application/octet-stream; charset=utf-8",
}
# Make POST request and catch results
file = {"file": f}
req = requests.post(
"https://cloud.memsource.com/web/api2/v1/projects/{project-id}/jobs",
headers=header,
files=file,
)
print(req.request.headers)
print(req.json())
The request header:
{
"User-Agent":"python-requests/2.27.1",
"Accept-Encoding":"gzip, deflate",
"Accept":"*/*",
"Connection":"keep-alive",
"Memsource":"{\"targetLangs\": [\"pl\"]}",
"Content-Disposition":"attachment; filename=\"test.txt\"",
"Authorization":"ApiToken {secret}",
"Content-Type":"application/octet-stream; charset=utf-8",
"Content-Length":"2902"
}
And the response from Memsource:
{
"asyncRequest":{
"action":"IMPORT_JOB",
"dateCreated":"2022-02-22T18:36:30+0000",
"id":"{id}"
},
"jobs":[
{
"workflowLevel":1,
"workflowStep":{
"uid":"{uid}",
"order":2,
"id":"{id}",
"name":"Tra"
},
"imported":false,
"dateCreated":"2022-02-22T18:36:30+0000",
"notificationIntervalInMinutes":-1,
"updateSourceDate":"None",
"dateDue":"2022-10-10T12:00:00+0000",
"targetLang":"pl",
"continuous":false,
"jobAssignedEmailTemplate":"None",
"uid":"{id}",
"status":"NEW",
"filename":"test.txt",
"sourceFileUid":"{id}",
"providers":[
]
}
],
"unsupportedFiles":[
]
}
both look okay to me...
I will appreciate any suggestions on how to get this thing working! :-)

I managed to fix this problem — noticed that requests are adding some limited headers to the body of the request, i.e., the content of the file passed in files parameter.
I simply got rid of that and changed the code as follows:
# Open the file to be uploaded and extract file name
with open(
"/file.ext", "rb"
) as f:
f_name = os.path.basename(f.name)
# Assemble the request header
header = {
"Memsource": json.dumps(Memsource_header),
"Content-Disposition": f'attachment; filename="{f_name}"',
"Authorization": f"ApiToken {self.authToken}",
"Content-Type": "application/octet-stream; charset=utf-8",
}
req = requests.post(
"https://cloud.memsource.com/web/api2/v1/projects/{project-id}/jobs",
headers=header,
data=f,
)

Related

How to download a Excel file from PHPExcel in Angular7 without saving it on server?

I'm using Codeigniter and PHPExcel to generate the .xls file. When I try to save it on server everything goes ok, but I don't want to save it on server, I want to download it when Angular 7 receive the response with the blob.
My Api with PHP is like this:
function getFile:
$this->objPHPExcel->getProperties()->setCreator("My File")->setLastModifiedBy("My File");
$this->objPHPExcel->getProperties()->setCreator("My File")->setTitle("");
$this->objPHPExcel->getProperties()->setCreator("My File")->setSubject("");
$this->objPHPExcel->getProperties()->setCreator("My File")->setDescription("");
$this->objPHPExcel->getProperties()->setCreator("My File")->setKeywords("");
Here I have a foreach creating the rows and cols:
$this->objPHPExcel->setActiveSheetIndex(0);
$this->objPHPExcel->getActiveSheet()->getColumnDimension($col)->setAutoSize(true);
$this->objPHPExcel->getActiveSheet()->setCellValue( $col.$linha , $val);
$this->objPHPExcel->getActiveSheet()->getStyle( $col.$linha )->getFont()->setBold( true );
$this->objPHPExcel->getActiveSheet()->getStyle( $col.$linha )->applyFromArray($centerStyle);
After foreach:
$objWriter = PHPExcel_IOFactory::createWriter($this->objPHPExcel, 'Excel5');
header('Content-type: application/vnd.ms-excel');
header('Content-Disposition: attachment; filename="file.xls"');
header('Cache-Control: max-age=0');
$objWriter->save('php://output');
Obs: If I set a path to the file it is created without problems:
$objWriter->save($filePath.'test.xls');
My angular 7 service is like this:
generateExcel(body){
const url = `${baseurl.base_url}api/generate_excel/get_file`;
const header = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded');
return this.http.post<any[]>(url, body, {headers: header});
}
And my subscribe in Component is like this:
this.generateExcelService.generateExcel(JSON.stringify(table)).subscribe((response: any) => {
let blob = new Blob([response], {type: 'application/vnd.ms-excel'})
saveAs(blob, 'file.xls')
})
The response is Blob I guess. Something like this:
But when the subscribe runs I got the following error in console:
Error: SyntaxError: Unexpected token � in JSON at position 0 at JSON.parse () at XMLHttpRequest.onLoad
I would like to know why the file isn't been downloaded when I click on the button which makes the subscribe runs and what do I have to do to make it works.
I found the solution for my problem here: Make Angular expect other response than JSON
What I did to solve my problem is to change my service to make it expect other response than JSON:
var HTTPOptions = {
headers: new HttpHeaders({
'Accept': 'text/html, application/xhtml+xml, */*',
'Content-Type': 'application/x-www-form-urlencoded'
}),
'responseType': 'blob' as 'json'
}
return this.http.post<any[]>(url, body, HTTPOptions);

Python 3.x - How to update a CSV file on Google Drive with requests patch

I am trying to update a .CSV file stored in a Google Drive folder with requests.patch(). The request succeeds and the file gets updated, but when I open it I see this.
I don't understand why 'Content-Disposition', 'Content-Type', etc. are added to my file. Is there any way I could update the .CSV without them?
My code:
headers = {'Authorization': token}
para = {'parents': 'folder_id'}
files = {
'data': ('metadata', json.dumps(para), 'application/json; charset=UTF-8'),
'file': open('file.csv', 'rb')
}
url = 'https://www.googleapis.com/upload/drive/v3/files/'+file_id+'?uploadType=media'
r=requests.patch(url, headers = headers, files = files)
print(r.text)
Any help is greatly appreciated.
It seems that the request body is the multipart upload. So how about modifying as follows?
From:
url = 'https://www.googleapis.com/upload/drive/v3/files/'+file_id+'?uploadType=media'
To:
url = 'https://www.googleapis.com/upload/drive/v3/files/'+file_id+'?uploadType=multipart'
Reference:
Performing a Multipart Upload
If this was not what you want, I'm sorry.
Added:
When you want to move the folder of the file, please modify as follows. para = {'parents': 'folder_id'} is not used in this case.
To:
url = 'https://www.googleapis.com/upload/drive/v3/files/'+file_id+'?uploadType=multipart&addParents=### new folderId ###&removeParents=### current folderId ###'
Reference:
Files: update

POST request to retrieve pdf in node.js

I am making a POST request to retrieve a pdf. The request works fine if I do it in postman, but I get an empty pdf if I do it through node.js using the request package. Here's my request using the request package:
let body = {
attr1: "attr1",
attr2: "attr2"
}
let opts = {
url: "some_url",
method: "post",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body
}
request(requestOpts).then(pdf => {
console.log(pdf) // prints out the binary version of the pdf file
fs.writeFileSync("testing.pdf", pdf);
});
I use the exact same request parameters when I use postman but it returns the pdf w/ the correct content.
Can someone help? Or is the way I am saving my pdf incorrect?
Thanks in advance!
Solution - i had to set encoding: false in the request options.
Try
fs.writeFileSync("testing.pdf", pdf, 'binary');
The third argument here tells fs to write binary rather than trying to UTF-8 encode it.
According to the docs the third paramter should be a string that represents the encoding.
For pdf files the encoding is 'application/pdf'
So this should work for you : fs.writeFileSync("testing.pdf", pdf, 'application/psf');

File Corruption when Uploading Excel files to Microsoft Graph API Beta

We're trying to upload Microsoft Excel file to OneDrive but the file gets corrupted every time we do so.
We've tried using [these instructions] to make a PUT request to the following permutations of settings:
Content-Encodings:
text/plan
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
POST bodies:
XLSX file bytes raw from disk
XLSX file encoded as UTF8 string
XLSX file encoded as base64
If we download the file that gets uploaded, it looks almost the same, but a few binary regions are different.
If you feel comfortable opening an Excel file off the internet, I've uploaded an example of the file we upload and the corrupted file OneDrive saves.
This has all the smell of a bug that can be fixed with a single parameter modification... I just can't figure out what it is.
Anyone have thoughts? Thanks!
Thanks #GSM. Here's our code in TypeScript.
var fileContent = FileSystem.readFileSync(localFile);
var url = `https://graph.microsoft.com/beta/me/drive/root/children/${doc.name}.xlsx:/content`,
var opts {
url: url,
method: 'PUT',
headers: [
'Content-Type': 'text/plain',
'Authorization': token
],
body: fileContent
};
var requestOpts = {
url: `https://${domain}${opts.path}`,
method: opts.method,
headers: {},
};
request(opts, cb);
The only difference I see is that you're using an alternate path to upload the file, which is also documented on the GraphAPI page. If we use the path you're using we get back the back the error message:
{
"error": {
"code": "BadRequest",
"message": "Entity only allows writes with a JSON Content-Type header.",
"innerError": {
"request-id": "2a2e7588-3217-4337-bee3-f8aff208510c",
"date": "2016-05-30T16:35:50"
}
}
}
..which is strange because it makes me expect that your code shouldn't have worked either.
Update -- the answer
By reading the file into a string and then writing it to the JSON object that defined the PUT parameters, we were corrupting it. We solved the problem by simply piping a file read stream right to the open HTTP request.
It would be easier to help if you posted your code.
However, here's some code that can be used to upload files to OneDrive. I tested it with your file and was able to upload and download just fine:
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + t.AccessToken);
var byteContent = File.ReadAllBytes(#"C:\Temp\sheet-uploaded.xlsx");
var url = resource + "beta/me/drive/root:/Documents/sheet-uploaded.xlsx:/content";
var result = client.PutAsync(url, new ByteArrayContent(byteContent)).Result;
result.Content.ReadAsStringAsync().Dump();
}

AlamofireImage Code=-1016 "Failed to validate response due to unacceptable content type"

I have gotten the URL of an image using Alamofire and SwityJSON to parse the response obtained from an API. Later, on my code, I am trying to use AlamofireImage to set that image to an ImageView... I am trying to do the following:
let headers = ["Authorization": requestToken, "Content-Type": "image/jpg"]
print(foods[indexPath.row].image)
Alamofire.request(.GET, imageEndPoint, headers: headers)
.responseImage { response in
debugPrint(response)
print(response.request)
print(response.response)
if let image = response.result.value {
print("image downloaded: \(image)")
}
}
However, when debugging, I am getting the following errors
Request]: <NSMutableURLRequest: 0x7fa93e052e60> { URL: http://159.203.92.55:9000/api/media/image?path=./server/uploads/images/products/jOqmy768a5wN2tcPd07cPhVH.jpg }
[Response]: <NSHTTPURLResponse: 0x7fa93e05a0a0> { URL: http://159.203.92.55:9000/api/media/image?path=./server/uploads/images/products/jOqmy768a5wN2tcPd07cPhVH.jpg } { status code: 200, headers {
Connection = "keep-alive";
"Content-Type" = "image/jpg";
Date = "Sun, 17 Jan 2016 16:28:38 GMT";
"Transfer-Encoding" = Identity;
"X-Powered-By" = Express;
} }
[Data]: 26224 bytes
[Result]: FAILURE: Error Domain=com.alamofire.error Code=-1016 "Failed to validate response due to unacceptable content type" UserInfo={NSLocalizedFailureReason=Failed to validate response due to unacceptable content type}
Optional(<NSMutableURLRequest: 0x7fa93e052e60> { URL: http://159.203.92.55:9000/api/media/image?path=./server/uploads/images/products/jOqmy768a5wN2tcPd07cPhVH.jpg })
Obviously there is an error due to the content type but I am not sure how to solve it:
[Result]: FAILURE: Error Domain=com.alamofire.error Code=-1016 "Failed to validate response due to unacceptable content type" UserInfo={NSLocalizedFailureReason=Failed to validate response due to unacceptable content type}
Because of this error, I can't go ahead and just set the image to the imageView
cell.thumbnailImageView.image = myImage
When I try to do the same requests using Chrome's Postman, I get the image without any issues.
Suggestions anyone? thanks
UPDATE: I found a way that is far from what I wanted but works
Alamofire.request(.GET, foods[indexPath.row].image, headers: headers)
.response { (request, response, data, error) in
debugPrint(response)
cell.thumbnailImageView.image = UIImage(data: data!, scale: 1)
}
this is vanilla Alamofire, so I still haven't been able to use AlamofireImage which provides better support for images. I got this method from the following post: How to load image in swift using Alamofire (skywinder's answer)
So I noticed the answer from the server was "Content-Type" = "image/jpg"; but as the user #Gomines pointed out, the correct content type for alamofireImage is actually "Content-Type": "image/jpeg"
Then I went back to AlamofireImages docs and read this
If the image you are attempting to download is an invalid MIME type not in the list, you can add custom acceptable content types using the addAcceptableImageContentTypes extension on the Request type.
So I just had to add this line of code before the AlamofireImage request:
Request.addAcceptableImageContentTypes(["image/jpg"])
The problem was that I was not matching the same content type that the server was responding with. However, that line solves the issue because now AlamofireImage will also accept the same type that the server is sending.
You made a mistake on the content type. It should be image/jpeg (http://www.sitepoint.com/web-foundations/mime-types-complete-list/).
let headers = ["Authorization": requestToken, "Content-Type": "image/jpeg"]
I hope this resolves the problem.

Resources