Download Excel file Zend framework - excel

When a user click a button I have to create an excel file (and this works perfect) but in the same time the user have to download the file that I've created before, but the download doesn't start...
Controller:
public function downloadAction() {
// I create the excel file .......
$this->_helper->layout->disableLayout();
$this->_helper->viewRenderer->setNoRender();
$this->getResponse()->setRawHeader( "Content-Type: application/vnd.ms-excel; charset=UTF-8" )
->setRawHeader( "Content-Disposition: attachment; filename=excel.xls" )
->setRawHeader( "Content-Transfer-Encoding: binary" )
->setRawHeader( "Expires: 0" )
->setRawHeader( "Cache-Control: must-revalidate, post-check=0, pre-check=0" )
->setRawHeader( "Pragma: public" )
->setRawHeader( "Content-Length: " . filesize( $filename ) )
->sendResponse();
echo json_encode(array(
'success' => 'true',
'xls' => 'http://'.$_SERVER['SERVER_NAME'].'/export/excel.xls'
));
exit();
}

Same time is not possible, as the content of the file is passed inside the body of the response with Content-Disposition header.
Yóu need to create 2 seperated calls.
After the client recieves the JSON Response, the client need to call another uri, maybe the one you just delivered to get the content of the file.
On a website you need to add an iframe into the page, which has src="http://'.$_SERVER['SERVER_NAME'].'/export/excel.xls".
<iframe src="http://'.$_SERVER['SERVER_NAME'].'/export/excel.xls"></iframe>
This will start the download inside the current page, else the page would be changed an an empty window will be shown.
Maybe use Javascript to generate the html for the iframe or just add a hidden iframe with id="downloadContainer" and just change the src for this iframe.
Another idea would be, sending the JSON data as header together with the file as body.
X-status: success
X-filename: //server.com/export/test.xls
But both params are not needed, as you could use default transfer specific params for it.
Why not use HTTP StatusCode for success/error status.
200 -Ok
201 -Created
400 -Bad request
404 -Not found
405 -Not allowed
500 -Server error
Set the HTTP Response Code inside Controller Action like that:
$this->getResponse()->setHttpResponseCode(405);
And for filename use your Content disposition header.
To send the file content just do:
readfile($absolute_path_to_file);
exit(1);

Related

Uploading Office Open XML files using API POST method in Python

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,
)

slim framework 3, fail to render html page

Am using twig-view to render the html, currently am unable to change the Content type returned to the browser, i can see that content type returned by slim as json instead of html content hence all codes are displayed in browser
$app->get('/',function(Request $req,Response $res,array $args) use ($app){
$container = $app->getContainer();
$container->view->render($res, 'test.html',[]);
});
enter image description here
Try to return the response like this:
return $container->view->render($res, 'test.html', []);
Addition to Zamrony P. Juhara
i have found that middleware i put was editing response to be returned as json content
->withHeader("Content-Type", "application/json")
/*
CORS
*/
$app->add(function ($req, $res, $next) {
$response = $next($req, $res);
return $response
//->withHeader("Content-Type", "application/json")
//->withHeader('Access-Control-Allow-Origin', 'http://localhost:2222/')
->withHeader('Access-Control-Allow-Origin', '*')
->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
->withHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,PATCH,OPTIONS');
});
so the browser was only getting json content response hence it was dumping all codes as json content instead of Content-Type: "text/html",
and that solved my problem

Attaching a file using Resumable upload w/ Gmail API

I am attempting to use Gmail's Resumable option for uploading attachments to an email. Documentation reference: https://developers.google.com/gmail/api/guides/uploads#resumable.
Currently I am able to send the email with the resumable URI, but without an attachment (using Postman). Documentation doesn't provide very clear examples of what the request should specifically look like, and there don't seem to be many examples after scouring the internet.
My requests are in two parts:
Initial request -
Request URL:
POST /upload/gmail/v1/users/me/messages/send?uploadType=resumable
Host: www.googleapis.c om (can't post links so I interrupted the url)
Headers:
Authorization: Bearer my_token_here
Content-Length: 113
Content-Type: application/json
X-Upload-Content-Length: 67
X-Upload-Content-Type: message/rfc822
Body:
{"raw":"VG86IG5pcnZhbmEucm9ja2VyQGdtYWlsLmNvbQpTdWJqZWN0OiBUZXN0RW1haWxTdWJqZWN0MwoKTWVzc2FnZSBjb250ZW50cyAjMy4"}
The body is a 64bit encoded string that include the To, Subject, and email message contents. Then gmail returns a response with an empty body, and a 'location' header that looks like the following: googleapis.com/upload/gmail/v1/users/me/messages/send?uploadType=resumable&upload_id=BRnB2UoAsKwzNMoQAy-JtmP6mu5agltqOWZ9uerI3k-KNTDJ73PWEjKuAHpko4RN6weSEysddH2kjj4G24uFw6E9oPv1XP69l7_KcmNuW-RAoz_5oS1T_4_E. (removed https:// because this account can only have one link in a post)
I then follow up with a PUT request to that URL returned in the location header.
The second request looks like the following:
Request URL:
PUT /upload/gmail/v1/users/me/messages/send?uploadType=resumable&upload_id=BRnB2UoAsKwzNMoQAy-JtmP6mu5agltqOWZ9uerI3k-KNTDJ73PWEjKuAHpko4RN6weSEysddH2kjj4G24uFw6E9oPv1XP69l7_KcmNuW-RAoz_5oS1T_4_E
Host: www.googleapis.c om
Headers:
Content-Length: 67
Content-Type: message/rfc822
Body:
{"raw":"VG86IG5pcnZhbmEucm9ja2VyQGdtYWlsLmNvbQpTdWJqZWN0OiBUZXN0RW1haWxTdWJqZWN0MwoKTWVzc2FnZSBjb250ZW50cyAjMy4"}
--- OR ---
I choose the binary option, and attach the file I am looking to upload via Postman.
I receive a response from Gmail with an object like this:
{
"id": "159d7ded3125e255",
"threadId": "159d7ded3125e255",
"labelIds": [
"SENT"
]
}
And an email is sent successfully, however there isn't an attachment with the email. When I show the original email in Gmail, there isn't any evidence of an attachment. Original looks like the following:
Received: from 325276275830 named unknown by gmailapi.google.com with HTTPREST; Wed, 25 Jan 2017 15:03:33 -0800
To: some.name#gmail.com
Subject: TestEmailSubject3
Date: Wed, 25 Jan 2017 15:03:33 -0800
Message-Id: <CEROA6F=0ohk33RD9XyC_gW1DZO88xYF4bXYqrCSct62MUuytDw#mail.gmail.com>
From: name_here#gmail.com
Message contents #3.
What am I missing? Do I need to encode some particular contents in a different way, or put some data in a different location? I'm not receiving any errors. I've been working on this for a few days now and I just can't figure it out.
I ran into the same problem, i made it work by using Nodemailer to create the email with the attachments, save the result to a file, then upload it with this.
Instead of send email in two parts you can send all your email data and attachment data as a mime message. like this.
I am using JavaScript client so you can do like this:
// MIME Mail Message data.
let mail = [
'Content-Type: multipart/mixed; boundary="foo_bar_baz"\r\n',
"MIME-Version: 1.0\r\n",
"to: to#gmail.com\r\n",
"from: from#gmail.com\r\n",
"subject: i am subject\r\n\r\n",
"--foo_bar_baz\r\n",
'Content-Type: text/plain; charset="UTF-8"\r\n',
"MIME-Version: 1.0\r\n",
"Content-Transfer-Encoding: 7bit\r\n\r\n",
"The actual message text goes here\r\n",
"--foo_bar_baz\r\n",
"Content-Type: application/json; name=package.json\r\n",
"Content-Transfer-Encoding: base64\r\n",
"Content-Disposition: attachment; filename=package.json\r\n\r\n",
"<base64 file data. data according to content type>",
"\r\n",
"--foo_bar_baz--",
].join("");
// get resumable upload link.
let resumableURL = "";
gapi.client
.request({
path: "/upload/gmail/v1/users/me/messages/send?uploadType=resumable",
headers: {
"X-Upload-Content-Type": "message/rfc822",
},
method: "post",
})
.then(
(res) => {
resumableURL = res.headers.location;
console.log(res);
},
(err) => console.log(err)
);
// send email
gapi.client
.request({
path: resumableURL,
headers: {
"Content-Type": "message/rfc822",
},
method: "post",
body: mail,
})
.then(
(res) => {
console.log(res);
},
(err) => console.log(err)
);
To convert gapi.client.request to Fetch API call you just need to add Authorization: Bearer <access_token> to header field. I have tried using Fetch API but response were blocked due to cors error so api client like Postman should be used.
To do more with resumable upload method check documentation: Upload Attachment

Serving image file with Buffers - corrupted image

I'm trying to get into Node.js, I'm following some tutorials and I'm now stuck on an example that explains how to create a simple web server to serve static files, using Buffers.
Here is the relevant code:
function serveStaticFile(file, res){
var rs = fs.createReadStream(file);
var ct = contentTypeForFile(file);
res.writeHead(200, { "Content-Type" : ct });
rs.on('readable', function(){
var d = rs.read();
if (d) {
if ( typeof d == 'string' )
res.write(d);
else if ( typeof d == 'object' && d instanceof Buffer )
res.write(d.toString('utf8'));
}
});
rs.on('error', function(e){
res.writeHead(404, { "Content-Type" : "application/json" });
var out = { error : "not_found", message : "'" + file + "' not found" };
res.end(JSON.stringify(out) + "\n");
});
rs.on('end', function(){
res.end();
});
}
the function serveStaticFile runs on incoming requests, file is the filename of the file you want to retrieve.
This is contentTypeForFile():
function contentTypeForFile(file){
var ext = path.extname(file);
switch(ext){
case '.html': return "text/html";
case '.js' : return "text/javascript";
case '.css' : return "text/css";
case '.jpg' :
case '.jpeg' : return "image/jpeg";
default : return "text/plain";
}
}
Running this command curl -o test.jpg http://localhost:8000/photo.jpg in the cmd actually downloads and creates a test.jpg file in my folder, the problem is that when I try to open the image the image itself seems to be corrupted.
What am I doing wrong? How can I convert the d.toString('utf8') back to an actual image once it is downloaded?
EDIT: I think the problem is that the response headers are saved toghether with the file content itself.. if I open the image file with a text editor, this is the content:
HTTP/1.1 200 OK
Content-Type: image/jpeg
Date: Sat, 30 Aug 2014 00:31:58 GMT
Connection: keep-alive
Transfer-Encoding: chunked
���� [ ... ]
How can I get rid of the headers while saving the file?
Thanks in advance, regards
Just remove the .toString('utf8'), you don't want or need it there. As a matter of fact, you can probably change this:
if (d) {
if ( typeof d == 'string' )
res.write(d);
else if ( typeof d == 'object' && d instanceof Buffer )
res.write(d.toString('utf8'));
}
To simply:
if (d) {
res.write(d);
}
res.write already handles checking the type of data you're passing to it and encoding it properly. The only time you need to worry about it is if you send it a string that is not utf8 encoded. From the docs:
This sends a chunk of the response body. This method may be called
multiple times to provide successive parts of the body.
chunk can be a string or a buffer. If chunk is a string, the second
parameter specifies how to encode it into a byte stream. By default
the encoding is utf8.
I just re-read second part of your question and realized that you said that you found headers in the test.jpg file when you opened it with a text editor. The only way I know of that that can happen when using curl is by specifying the -i option. Something like: curl -i -o test.jpg http://localhost:8000/photo.jpg. For the most part, I wouldn't recommend using that option with curl since it will corrupt any binary files it downloads. There are certainly use cases for it, but this isn't one of them.
Regardless of what was going on with curl, it's important to know that the server code has nothing to do with what the client side does with the headers it receives. Once the server sends the headers (and the content for that matter), it's completely out of its hands. I can not think of any way the code on the server could force the client to save headers in a file along with the actual content; that's completely up to the client.
Note: If you want to see the headers curl is receiving (and sending for that matter), try the -v option. Received headers will be prefixed with < and sent with >; lines prefixed with * have to do with the connection itself. It looks something like this:
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8000
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/plain
< Date: Sat, 30 Aug 2014 15:54:14 GMT
< Connection: keep-alive
< Transfer-Encoding: chunked
<
[data not shown]

non-chunked multipart/mixed POST?

I'm trying to send multiple binary files to a web service in a single multipart/mixed POST but can't seem to get it to work... target server rejects it. One thing I noticed is Node is trying to do the encoding as chunked, which I don't want:
POST /SomeFiles HTTP/1.1
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=123456789012345
Host: host.server.com
Connection: keep-alive
Transfer-Encoding: chunked
How do I get it to stop being chunked? Docs say that can be disabled by setting the Content-Length in the main request header but I can't set the Content-Length since, among other reasons, I'm loading one file at a time -- but it shouldn't be required either since it's multipart.
I think the rest is OK (excepting it's not chunked, unless the req_post.write() is doing that part), e.g. for the initial part I:
var req_post = https.request({
hostname: 'host.server.com',
path: '/ManyFiles',
method: 'POST',
headers: {
'MIME-Version': '1.0',
'Content-Type': 'multipart/mixed; boundary=' + boundary
}
},...);
and then pseudo-code:
while ( true ) {
// { get next file here }
req_post.write('\r\n--' + boundary + '\r\nContent-Type: ' + filetype + '\r\nContent-Length: ' + filesize + '\r\n\r\n');
req_post.write(chunk);// filesize
if ( end ) {
req_post.write('\r\n--' + boundary + '--\r\n');
req_post.end();
break;
}
}
Any help/pointers is appreciated!
The quick answer is you cannot disable chunked without setting content-length. The chunked encoding was introduced for cases where you do not know the size of the payload when you start transmitting. Originally, content-length was required and the recipient knew it had the full message when it received content-length bytes. Chunked encoding removed that requirement by transmitting mini-payloads each with a content-length, followed by a zero-size to denote completion of the payload. So, if you do not set the content-length, and you do not use the chunked methodology, the recipient will never know when it has the full payload.
To help solve your problem, if you cannot send chunked and do not want to read all the files before sending, take a look at fs.stat. You can use it to get the file size without reading the file.

Resources