Symfony is adding to file size when downloading from controller - symfony

I have uploaded an image with the VichUploaderBundle am returning an image file like so:
/**
* Show license image.
*
* #param Request $request
* #return Response
*/
public function showImageAction(Request $request,$id)
{
$em = $this->getDoctrine()->getManager();
$license = $em->getRepository('MyCarBundle:License')->findOneById($id);
$fileName = $license->getFrontName();
$user = $this->getUser();
$contents = '';
$filePath = __DIR__ . '/../../../../app/uploads/licenses/'. $user->getId().'/'.$fileName;
$response = new Response();
$disposition = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_INLINE, $fileName);
$response->headers->set('Content-Disposition', $disposition);
$response->headers->set('Content-Type', 'image/jpg');
$response->setContent(file_get_contents($filePath));
return $response;
}
and in html im trying to show the image, like so
<img class="img-fluid w-100 u-block-hover__main--zoom-v1" src="{{ path('license_show_image',{'id':license.id}) }}" alt="Image Description">
The image was uploaded succesfully. When I open the image in the uploaded folder, it is showing fine.
But the image is not showing in the browser. while directly accessing the url, the image is downloading but it is not able to open. It is giving me the following error:
The file “ODL_Eng_Full_Back-5.jpg” could not be opened.
Does anyone know whats going on?
UPDATE:
Symfony is adding ~700kb to the file size when downloading. after uploading it seems to have proper file size, but downloading adds file size. whats going on here?
UPDATE 2:
these are the headers:
Request URL:http://localhost/app-temp/web/app_dev.php/account/license/2/show-image
Request Method:GET
Status Code:200 OK
Remote Address:[::1]:80
Referrer Policy:no-referrer-when-downgrade
Response Headers
view source
Cache-Control:no-cache, private
Connection:Keep-Alive
Content-Disposition:inline; filename="ODL_Eng_Full_Back.jpg"
Content-Type:image/jpg
Date:Tue, 17 Oct 2017 23:31:02 GMT
Keep-Alive:timeout=5, max=100
Server:Apache/2.4.27 (Unix) PHP/7.1.8
Transfer-Encoding:chunked
X-Debug-Token:0e53c1
X-Debug-Token-Link:http://localhost/app-temp/web/app_dev.php/_profiler/0e53c1
X-Powered-By:PHP/7.1.8
Request Headers
view source
Accept:image/webp,image/apng,image/*,*/*;q=0.8
Accept-Encoding:gzip, deflate, br
Accept-Language:en-GB,en-US;q=0.8,en;q=0.6
Connection:keep-alive
Cookie:__atuvc=54%7C42; _ga=GA1.1.1906437920.1508087850; _gid=GA1.1.1258608977.1508087850; PHPSESSID=iuhk0pr0oqtaje5371fp7q7vfk
Host:localhost
Referer:http://localhost/app-temp/web/app_dev.php/account/creditcards/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36

You can use BinaryFileResponse (doc
) like:
$response = new BinaryFileResponse($filepath);
$response->headers->set('Content-disposition', sprintf('filename=%s', basename($filepath)));
return $response;

EDIT : HTTP Response seems to be correct.
Since you're using VichUploaderBundle, why not just use its helpers ?
<img src="{{ vich_uploader_asset(license, 'frontName') }}" alt="{{ license.frontName }}">

Related

Streaming mp4 requests via http with range header in grails

I'm on an old grails 2.5.1 app and I noticed mp4 video files served from the server don't play in Safari. I looked up the issue on SO and got some hints that it has to do with the range header. But I suspect the way I'm handling the range header isn't quite right.
So far, what I've found is Mac OS Safari 11.0 (11604.1.38.1.7) (I don't care about ios Safari right now) sends two GET requests. Firstly, it sends one with:
host: localhost:8080
accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Safari/604.1.38
accept-language: en-us
accept-encoding: gzip, deflate
x-request-time: t=****
x-forwarded-for: *.*.*.*
x-forwarded-host: *.com
x-forwarded-server: *.com
connection: Keep-Alive
cookie: ...TOO BIG TO SHOW HERE
<- "GET /.../videos/lol.mp4 HTTP/1.1" 200 186ms
Subsequently, it sends second GET request:
host: localhost:8080
language: en-us
playback-session-id: 03F1B4E6-F97E-****
bytes=0-1
accept: */*
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Safari/604.1.38
https://.../videos/lol.mp4
encoding: identity
request-time: t=****
forwarded-for: *.*.*.*
forwarded-host: *.com
forwarded-server: *.com
connection: Keep-Alive
cookie: ...TOO BIG TO SHOW HERE
<- "GET /uiv2/videos/lol.mp4 HTTP/1.1" 206 149ms
Debugging this is hard because Safari web inspector doesn't show you much. In fact, it doesn't even show you all the headers it sends so I had to get this from the back end.
As can be seen, the difference between request 1 and 2 is the 2nd has playback-session-id and range.
The hard part is finding out how to please Safari in how range and playback-session-id are handled.
I've made a controller to return the range of bytes requested, if they're requested. But still no luck.
import grails.compiler.GrailsTypeChecked
import grails.plugin.springsecurity.annotation.Secured
import asset.pipeline.grails.AssetResourceLocator
import grails.util.BuildSettings
import org.codehaus.groovy.grails.commons.GrailsApplication
import org.springframework.core.io.Resource
class VideoController {
GrailsApplication grailsApplication
AssetResourceLocator assetResourceLocator
public index() {
Resource mp4Resource = assetResourceLocator.findAssetForURI('/../lol.mp4');
response.addHeader("Content-type", "video/mp4")
response.addHeader( 'Accept-Ranges', 'bytes')
String range = request.getHeader('range')
if(range) {
String[] rangeKeyValue = range.split('=')
String[] rangeEnds = rangeKeyValue[1].split('-')
if(rangeEnds.length > 1) {
int startByte = Integer.parseInt(rangeEnds[0])
int endByte = Integer.parseInt(rangeEnds[1])
int contentLength = (endByte - startByte) + 1
byte[] inputBytes = new byte[contentLength]
mp4Resource.inputStream.read(inputBytes, startByte, contentLength)
response.status = 206
response.addHeader( 'Content-Length', "${contentLength}")
response.outputStream << inputBytes
} else {
response.addHeader( 'Content-Length', "${mp4Resource.contentLength()}")
response.outputStream << mp4Resource.inputStream
}
} else {
log.info 'no range, so responding with whole mp4'
response.addHeader( 'Content-Length', "${mp4Resource.contentLength()}")
response.outputStream << mp4Resource.inputStream
}
}
}
In the Safari console, I get:
Failed to load resource: Plug-in handled load
Nothing else. And sadly lots of fields in the web inspector are blank even though they're obviously set in the server.
I've tried so many things at this point that any help, pointers, tips will be appreciated. Thanks guys :) !
After trying many things and scouring many posts, this formula worked. You need all four of those headers. Don't need to return anything in the first request. This may not work for all browsers but this works for safari. Additional modifications can ensure all browsers are handled
class VideoController {
GrailsApplication grailsApplication
AssetResourceLocator assetResourceLocator
public index() {
Resource mp4Resource = assetResourceLocator.findAssetForURI('/../lol.mp4')
String range = request.getHeader('range')
if(range) {
String[] rangeKeyValue = range.split('=')
String[] rangeEnds = rangeKeyValue[1].split('-')
if(rangeEnds.length > 1) {
int startByte = Integer.parseInt(rangeEnds[0])
int endByte = Integer.parseInt(rangeEnds[1])
int contentLength = (endByte - startByte) + 1
byte[] inputBytes = new byte[contentLength]
def inputStream = mp4Resource.inputStream
inputStream.skip(startByte) // input stream always starts at the first byte, so skip bytes until you get to the start of the requested range
inputStream.read(inputBytes, 0, contentLength) // read from the first non-skipped byte
response.reset() // Clears any data that exists in the buffer as well as the status code and headers
response.status = 206
response.addHeader("Content-Type", "video/mp4")
response.addHeader( 'Accept-Ranges', 'bytes')
response.addHeader('Content-Range', "bytes ${startByte}-${endByte}/${mp4Resource.contentLength()}")
response.addHeader( 'Content-Length', "${contentLength}")
response.outputStream << inputBytes
}
}
}
}

Download CSV File by posting JSON data in ASP.NET MVC

In ASP.NET MVC, I am trying to download a file via a post and sending JSON data. This JSON data are filters for the data being displayed on the page via knockout.js. The critieria object is always null. How can I download a file, by sending post data via javascript or a form post? Ive accomplished an ajax download by using a GET, but now I have extra data, like arrays I need to post.
Form
<form method="POST" action="#Model.ExportUrl" >
<input type="hidden" name="criteria" data-bind="value: ko.toJSON(data())" />
<button class="btn"><i class="icon-download-alt"></i> Export</button>
</form>
Request
Request URL:http://localhost:2222/members/eventteams/export?eventId=8998
Request Method:POST
Status Code:500 Internal Server Error
Request Headersview source
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
Content-Length:128
Content-Type:application/x-www-form-urlencoded
Host:localhost:2222
Origin:http://localhost:2222
Referer:http://localhost:2222/members
User-Agent:Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36
Query String Parametersview sourceview URL encoded
eventId:8998
Form Dataview sourceview URL encoded
criteria:{"page":1,"pageSize":"100","sortOrder":"Team.Name","sortDirection":"ASC"}
Controller
[HttpPost]
public virtual ActionResult Export(int eventId, DivisionTeamsTableCriteria criteria)
{
You can try posting the form to Iframe like this one
How do you post to an iframe?
And on the iframe asp.net page, you write the File to response like this
Write to CSV file and export it?
Iframe can be 1x1 pixels
Using knockout.js I created this custom binding that works quite well.
ko.bindingHandlers.download = {
init: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
id = 'download-iframe-container',
iframe;
$(element).unbind('click').bind('click', function () {
iframe = document.getElementById(id);
if (!iframe) {
iframe = document.createElement("iframe");
iframe.id = id;
iframe.style.display = "none";
}
if (value.data) {
iframe.src = value.url + (value.url.indexOf('?') > 0 ? '&' : '?') + $.param(ko.mapping.toJS(value.data));
} else {
iframe.src = value.url;
}
document.body.appendChild(iframe);
return false;
});
}
};

using swift mailer of symfony 2, got an email but getting unnecessary response in email

getting this response by email "HTTP/1.0 200 OK Cache-Control: no-cache Content-Type: text/html; charset=UTF-8 Date: Tue, 13 Nov 2012 04:56:14 GMT".
here is my code:
public function sendEmail($subject, $template, $templateParams)
{
$userEmail = $this->session->get('email');
$name = $this->session->get('name');
$adminEmail = $this->container;
$templateParams['username'] = $name;
$message = \Swift_Message::newInstance()
->setSubject($subject)
->setFrom($adminEmail)
->setTo($userEmail)
->setBody($this->templating->render('SocialDonSocialBundle:Email:'.$template,$templateParams), 'text/html');
$this->mailer->send($message);
Also note that this method is belongs to a service namely "Email". I have created a service "Email " which responsible to send emails. Does anybody know what might be the issue??
You need to use renderView() instead of render(), because render() always display the header.
In newer versions of Symfony2 like version 2.5.*
The solution is to use renderResponse and do a getContent() on it :
$content = $this->container->get('templating')->renderResponse(
'YOUR_TEMPLATE.twig',
templateParams)
)->getContent();
or with the same values like in the question :
$this->templating->renderResponse('SocialDonSocialBundle:Email:'.$template,$templateParams)->getContent();

Additional mailer service to use the spool and send instant emails in Symfony2 - strange headers

by default I use spool mailing solution for sending newsletter in my web page. But I also need to send email immediately. So I have used this solution
If I send newsletter with Spool everything is fine. But when I use
$mailer = $this->get('instant_mailer');
I receive email with some text prepend at the beginning:
HTTP/1.0 200 OK Cache-Control: no-cache Content-Type: text/html; charset=UTF-8 Date: Fri, 07 Sep 2012 16:19:06 GMT
How to remove this?
I bet that you're trying to send a Response object.
new Response();
it goes to __toString ()
public function __toString()
{
$this->prepare();
return
sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n".
$this->headers."\r\n".
$this->getContent();
}
It is because:
$this->render('template.html.twig');
returns Response to avoid that use:
$response = $this->render('template.html.twig');
$text = $response->getContent();
Regards,
Max
Use
$content = $this->renderView('template.html.twig');
instead of
$content = $this->render('template.html.twig');
render returns a response
Other posible solution to the problem is to use templating service instead of $this->render():
<?php
$body = $this->get('templating')->render('template.html.twig');

doPost not getting called by embedded Jetty, when using Context collection

I am using Jetty 6 in embedded mode. I have a number of servlets in a ContextHandlerCollection. Beyond this problem, the servlets work fine on their different URLs.
ContextHandlerCollection contexts = new ContextHandlerCollection();
server.setHandler(contexts);
HandlerContainer mainhandler = contexts;
Context qxrpc = new Context(contexts,"/api",Context.SESSIONS);
ServletHolder rpcServHolder = new ServletHolder(new FrzRpcServlet());
rpcServHolder.setInitParameter("referrerCheck", "public");
// allows cross-domain calls
qxrpc.addServlet( rpcServHolder, "*.qxrpc");
Context statscontext =new Context(contexts,"/stats",Context.SESSIONS);
ServletHolder statsHolder = new ServletHolder(new FrzStatsServlet());
statsHolder.setInitParameter("restrictToLocalhost", "false");
// allows cross-domain calls
statscontext.addServlet(statsHolder, "/*");
Context hellocontext = new Context(contexts,"/hello", Context.SESSIONS);
hellocontext.addServlet(new ServletHolder(new HelloServlet("HELLO TEST: ")),
"/*");
Context zdbcontext = new Context(contexts,"/zdb", Context.ALL);
ServletHolder zdbHolder = new ServletHolder(new FrzZdbServlet());
statsHolder.setInitParameter("restrictToLocalhost", "false");
// allows cross-domain calls
zdbcontext.addServlet(zdbHolder, "/*");
Context root = new Context(mainhandler,"/",Context.SESSIONS);
root.setResourceBase(docroot);
root.addServlet(DefaultServlet.class, "/");
I know the POST request is coming across to my server. Here is some ngrep output:
T 127.0.0.1:51634 -> 127.0.0.1:8080 [AP]
GET /zdb/test.123:1.1.local1.stringtest HTTP/1.1..Host: 127.0.0.1:8080..Connection: keep-alive..Referer: http://127.0.0.1:8888/GWT_ZDB_editor.html?gwt.codesvr=127.0.0.1:9997..Origin: http://127.0.0.1:8888..User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.71 Safari/534.24..Content-Type: text/plain; charset=utf-8..Accept: */*..Accept-Encoding: gzip,deflate,sdch..Accept-Language: en-US,en;q=0.8..Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3....
##
T 127.0.0.1:8080 -> 127.0.0.1:51634 [AP]
HTTP/1.1 200 OK..Access-Control-Allow-Origin: *..Content-Type: application/json; charset=ISO-8859-1..Content-Length: 124..Server: Jetty(6.1.15)....
##
T 127.0.0.1:8080 -> 127.0.0.1:51634 [AP]
{ "r":0,"D":"test.123:1.1.local1.stringtest","m":"OK","t":0,"p": {"ztype": "STRING", "dat" : { "cp":0, "v": "test12131" }}}
##
Unsuccessful POST - reports 200 OK - but never gets to servlet
T 127.0.0.1:51634 -> 127.0.0.1:8080 [AP]
OPTIONS /zdb/test.123:1.1.local1.stringtest/put HTTP/1.1..Host: 127.0.0.1:8080..Connection: keep-alive..Referer: http://127.0.0.1:8888/GWT_ZDB_editor.html?gwt.codesvr=127.0.0.1:9997..Access-Control-Request-Method: POST..Origin: http://127.0.0.1:8888..User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.71 Safari/534.24..Access-Control-Request-Headers: content-type..Accept: */*..Accept-Encoding: gzip,deflate,sdch..Accept-Language: en-US,en;q=0.8..Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3....
#
T 127.0.0.1:8080 -> 127.0.0.1:51634 [AP]
HTTP/1.1 200 OK..Allow: GET, HEAD, POST, TRACE, OPTIONS..Content-Length: 0..Server: Jetty(6.1.15)...
.
What I can't figure out is why the doPost() is not getting called, while the doGet() is. The servlet in question is the FrzZdbServlet.
Found a number of threads on Google, but the Jetty folks only point back to examples, which in turn only implement do doGet() for the Context examples. As in here
Also, I am posting from GWT code, and I am using content-type application/json. Could this be the issue? Any pointers would be appreciated.
My Context apparently did not accept POSTs with content-type: application/json. Removing this on my client code fixed it. If anyone else has input appreciate it.

Resources