response.sendFilePartial

326 views
Skip to first unread message

omid pourhadi

unread,
Oct 3, 2016, 9:23:44 AM10/3/16
to vert.x
Hi,

I'm trying to simulate sending partial content to web browser and get this error


  
 response.setStatusCode(206);
            response
.bodyEndHandler(r -> {
               
//send part 2
                response
.sendFile(fpath, 1755435, 3510870);
               
           
});
           
//send part 1
            response
.sendFile(fpath, 0, 1755435, rh -> {
               
if (rh.succeeded())
               
{
                   
System.out.println("suc");
               
}
               
else
                   
System.out.println(rh.cause());
               
});



java.lang.IllegalStateException: Head already written
    at io.vertx.core.http.impl.HttpServerResponseImpl.doSendFile(HttpServerResponseImpl.java:436)


the full source code is here :

what is the correct way to find out response.sendFile for part 1 is completed then send another part of a file till the last part?

Paulo Lopes

unread,
Oct 3, 2016, 9:58:52 AM10/3/16
to vert.x
The exception is coming from the bodyEndHandler because you're writing to the response **after** the response has ended (that is what the end handler does).

Since this API will close the connection on success if you need to send multiple files concatenated you can use a Pump and pump all files, readstream's to the response object which is a writestream.

omid pourhadi

unread,
Oct 3, 2016, 11:07:36 AM10/3/16
to vert.x
Thank you for your guideline.

I change my code and it works but I don't know why I want to make sure it is correct.

HttpServerRequest request = ctx.request();
           
HttpServerResponse response = request.response();
           
String fpath = "/home/omidp/Videos/big_buck_bunny.mp4";
           
// download video from
           
// http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4
           
Path filepath = new File(fpath).toPath();
           
Long length = Files.size(filepath);
           
String fileName = filepath.getFileName().toString();
           
FileTime lastModifiedObj = Files.getLastModifiedTime(filepath);
            response
.putHeader("Content-Type", "video/mp4");
            response
.putHeader("Content-Disposition", "inline;filename=\"" + fileName + "\"");
            response
.putHeader("Accept-Ranges", "bytes");
            response
.putHeader("ETag", fileName);
           
Range full = new Range(0, length - 1, length);        
            response
.putHeader("Content-Length", String.valueOf(full.length));
            response
.setStatusCode(206);// partial content
           
           
           
           
ByteArrayOutputStream os = new ByteArrayOutputStream();

//copy first part
           
Range.copy(new FileInputStream(new File(fpath)), os, full.total, 0, 1755435);
           
            response
.write(Buffer.buffer(os.toByteArray()));
           
Pump pump = Pump.pump(request, response);
            pump
.start();
            pump
.stop();
           
//
//            Thread.sleep(5000);

//copy second part
            os
= new ByteArrayOutputStream();
           
Range.copy(new FileInputStream(new File(fpath)), os, full.total, 1755435, 2755435);
            response
.write(Buffer.buffer(os.toByteArray()));            
            pump
.start();
            pump
.stop();
//should I call this it works without calling response.end ?
            response
.end();


Paulo Lopes

unread,
Oct 4, 2016, 6:59:23 AM10/4/16
to vert.x
l still do not understand what you're trying to achieve.

If you're just aiming for http range support you should better do it using the static handler on vert.x web than reimplement it yourself.

There are a couple of remarks to the code you show:

You're claiming that you're sending partial content but it does seem you're sending the full file in 2 chunks.

You're also loading the full file into memory by copying the 2 chunks to a buffer, in the case of a video like you have and say hundreds of requests I'd bet you'll run out of memory very quickly.

You're are starting and stoping the pump as a sequence which means that the pump might not have completely moved the data from the origin to the destination by the time you stop.

Finally you should call response.end() otherwise you will keep the connection to your client open until there is a connection timeout.

omid pourhadi

unread,
Oct 4, 2016, 8:28:43 AM10/4/16
to vert.x
Hi,

I want to handle partial content for video streaming to be exact write partial content to the response with vert.x via  Accept range header (Implementing HTTP byte-range requests).

let’s create a scenario. Say I want to request a very large video file from a vert.x http service, which I do by sending a standard GET request including a Range header that specifies the range of bytes I’m requesting, like so: Range: bytes=0-999. The service should then respond with with a 206 Partial Content status code, a Content-Range header, and the requested range of bytes.


From then on, it’s a matter of the client continuing to make requests until it has retrieved all of the bytes it wishes to get from the resource. If the client makes a range request that is out of bounds—that is, none of the range values overlap the extent of the resource—the service should respond with a 416 Requested Range Not Satisfiable status.

Here’s a real example making range requests with Flickr.


GET /2390/2253727548_a413c88ab3_s.jpg HTTP/1.1
Host: farm3.static.flickr.com
Range: bytes=0-999
 
HTTP
/1.0 206 Partial Content
Date: Mon, 05 May 2008 00:36:57 GMT
Server: Apache/2.0.52 (Red Hat)
Accept-Ranges: bytes
Content-Length: 1000
Content-Range: bytes 0-999/3980
Content-Type: image/jpeg


GET /2390/2253727548_a413c88ab3_s.jpg HTTP/1.1
Host: farm3.static.flickr.com
Range: bytes=1000-
 
HTTP
/1.0 206 Partial Content
Date: Mon, 05 May 2008 00:37:54 GMT
Server: Apache/2.0.52 (Red Hat)
Accept-Ranges: bytes
Content-Length: 2980
Content-Range: bytes 1000-3979/3980
Content-Type: image/jpeg

There is also a Java implementation here which does what I explained but I want to implement it with vert.x.

Thank you for your time and consideration.

Paulo Lopes

unread,
Oct 4, 2016, 10:40:08 AM10/4/16
to vert.x
The static handler already supports this. If you use it you save yourself the trouble of re-implement it.

You need to enable it:

StaticHandler.create().setEnableRangeSupport(true);

And you can see how it works by seeing the unit tests:

https://github.com/vert-x3/vertx-web/blob/master/vertx-web/src/test/java/io/vertx/ext/web/handler/StaticHandlerTest.java#L488
Reply all
Reply to author
Forward
0 new messages