Wish HttpServer SendBytes method

Discussion in 'Bugs & wishlist' started by warwound, Jan 1, 2014.

  1. warwound

    warwound Expert Licensed User

    I am using the HttpServer library and want to create JPG and/or PNG images on the fly and return them from the HttpServer HandleRequest event handling sub.

    The ServletResponse object has SendFile and SendString methods - i think i need a SendBytes method.
    This is my HandleRequest sub:

    Code:
    Sub HttpServer1_HandleRequest (Request As ServletRequest, Response As ServletResponse)
       
    Dim Paths() As String=Regex.Split("/", Request.RequestURI)
       
       
    If Paths.Length=5 AND Paths(1)="tile" Then
         
    '   a tile request to serve
         Dim Zoom As Int=Paths(2)
         
    Dim TileX As Int=Paths(3)
         
    Dim Temp() As String=Regex.Split("\.", Paths(4))
         
    Dim TileY As Int=Temp(0)
         
         
    Dim TileType As String="PNG"
         
    If Temp(1)="jpg" Then
           TileType=
    "JPEG"
         
    End If
         
         
    Dim North As Double=Mercator1.YToLat((TileY*256)-CIRCLE_RADIUS, Zoom)
         
    Dim South As Double=Mercator1.YToLat(((TileY+1)*256)+CIRCLE_RADIUS, Zoom)
         
    Dim West As Double=Mercator1.XToLon((TileX*256)-CIRCLE_RADIUS, Zoom)
         
    Dim East As Double=Mercator1.XToLon(((TileX+1)*256)+CIRCLE_RADIUS, Zoom)
         
         
    Dim SpatialiteStatement As Spatialite_Stmt
         SpatialiteStatement=SpatialiteDatabase.Prepare(
    "SELECT X(Geometry) AS Longitude, Y(Geometry) AS Latitude FROM "&TABLE_NAME&" WHERE MBRWithin(Geometry, BuildMBR(?, ?, ?, ?, ?));")   '   ORDER BY?
         
         SpatialiteStatement.BindDouble(
    1, West)
         SpatialiteStatement.BindDouble(
    2, North)
         SpatialiteStatement.BindDouble(
    3, East)
         SpatialiteStatement.BindDouble(
    4, South)
         SpatialiteStatement.BindInt(
    5, SRID)
         
         
    Dim Tile As Bitmap
         Tile.InitializeMutable(
    256256)
         
    Dim Canvas1 As Canvas
         Canvas1.Initialize2(Tile)
         
         
    Dim X As Int
         
    Dim Y As Int
         
    Dim Latitude As Double
         
    Dim Longitude As Double
         
         
    Dim Count As Int=0
         
    Do While SpatialiteStatement.Step
           Count=Count+
    1
           Latitude=SpatialiteStatement.ColumnDouble(
    1)
           Longitude=SpatialiteStatement.ColumnDouble(
    0)
           X=Mercator1.LonToX(Longitude, Zoom)-(TileX*
    256)
           Y=Mercator1.LatToY(Latitude, Zoom)-(TileY*
    256)
           Canvas1.DrawCircle(X, Y, CIRCLE_RADIUS, CIRCLE_COLOR, CIRCLE_FILL, CIRCLE_STROKE_WIDTH)
         
    Loop
         
    Log("Count: "&Count)
         
         
    ' here i have to save the image to a temp file
         TileId=TileId+1
         
    Dim TempFileName As String="tile-"&TileId&"."&Temp(1)
         
    Dim TempFile As OutputStream=File.OpenOutput(File.DirInternalCache, TempFileName, False)
         Tile.WriteToStream(TempFile, 
    100, TileType)
         TempFile.Close
         
         
    If TileType="PNG" Then
           Response.SetContentType(
    "image/png")
         
    Else
           Response.SetContentType(
    "image/jpeg")
         
    End If
         
         Response.SendFile(
    File.DirInternalCache, TempFileName)
         
    File.Delete(File.DirInternalCache, TempFileName)
         
         
    Return
         
       
    End If
       
       Response.SetContentType(
    "text/html")
       Response.SendString(
    "No valid tile request received")
    End Sub
    So i have a Bitmap named Tile that i wish to serve as a JPG or PNG image, currently i'm writing it to a file and using the ServletResponse SendFile method to serve it.
    Ideally i'd be able to use WriteToStream to write the image to an OutputStream then send the OutputStream bytes with no need for the temporary file.

    Is this possible - can a SendBytes method be added to the ServletResponse?

    Thanks.

    Martin.
     
  2. Erel

    Erel Administrator Staff Member Licensed User

    You should use SendFile with a temporary file. The overhead will be minimal and you will not get out of memory errors.
     
  3. warwound

    warwound Expert Licensed User

    Ok using a temp file is acceptable.
    But i have a problem writing the temp file to File.DirDefaultExternal on my Galaxy S3 running android 4.1.2.
    This is the code that creates the temp file and returns the Bitmap as the server response:

    Code:
    TileId=TileId+1
    Dim TempFileName As String="tile-"&TileId&"."&FileExtension
    Dim TempFile As OutputStream=File.OpenOutput(File.DirDefaultExternal, TempFileName, False)
    Tile.WriteToStream(TempFile, TILE_COMPRESSION_LEVEL, TileType)
    TempFile.Close
    Response.SetContentType(ContentType)
    Response.SendFile(
    File.DirDefaultExternal, TempFileName)
    File.Delete(File.DirDefaultExternal, TempFileName)
    TileId is a process global integer, it's used to ensure each temp file has a unique filename.

    On my S3 this always fails with many exceptions of this type:

    Looks as though the code fails to write the temp file and then an exception is thrown when it tries to read the non-existent file.
    The S3 has an SD card inserted and there's plenty of free space on both built in external memory and the SD card.
    Using a file explorer i find no temp files in the built in external memory File.DirDefaultExternal location or the same location on SD card.

    The same code works perfectly on an old HTC Desire S.

    Saving the temp file to File.DirInternal or File.DirInternalCache works on all devices tested so far.

    Any idea why the S3 fails to write the temp file?

    Martin.
     
  4. thedesolatesoul

    thedesolatesoul Expert Licensed User

    Are you running compile to lib?
    I had some problems, because the write was in a library and the relevant permissions were not being added.
    Best to see if you can find the write exception happening.
     
  5. warwound

    warwound Expert Licensed User

    No i'm not compiling to a library.

    I have a service module running the HttpServer library and in the HttpServer library HandleRequest event i (try to) write this temp file.
    The manifest shows that the required 'android.permission.WRITE_EXTERNAL_STORAGE' permission has been added.

    As i said it works on an old HTC Desire S - which has no built in external memory.

    I just tested the code on my Tab2 - again it fails with the same exceptions as on the S3.
    On the S3 and Tab2 it seems as though the temp file isn't written to external memory.
    Both the S3 and Tab2 have built in external memory and both the S3 and Tab2 have plenty of available space on both built in external memory and SD card.

    Martin.
     
  6. warwound

    warwound Expert Licensed User

    I just had a thought that might be relevant.

    The service module that contains the HttpServer and fails to write the temp tile does successfully copy a database file from Assets to DirDefaultExternal in Service_Start.
    That File operation works yet in the same service module writing the temp file fails in the HttpServer HandleRequest event.

    Martin.
     
  7. thedesolatesoul

    thedesolatesoul Expert Licensed User

    Try changing TileType, or adding a flush before close?
     
  8. warwound

    warwound Expert Licensed User

    Still no success.

    I updated my code that creates the temp file, calling the OutputStream Flush method before calling it's Close method.
    I also added a Log statement to log the name of the temp file i'm trying to create.
    And then i added a little code to create a plain text file - but only to create the text file once:

    Code:
    TileId=TileId+1
    Dim TempFileName As String="tile-"&TileId&"."&FileExtension

    Log("TempFileName: "&TempFileName&", TileType: "&TileType)

    Dim OutputStream1 As OutputStream=File.OpenOutput(File.DirDefaultExternal, TempFileName, False)
    Tile.WriteToStream(OutputStream1, TILE_COMPRESSION_LEVEL, TileType)
    OutputStream1.Flush
    OutputStream1.Close
    Response.SetContentType(ContentType)
    Response.SendFile(
    File.DirDefaultExternal, TempFileName)
    File.Delete(File.DirDefaultExternal, TempFileName)

    If TileId=2 Then
       
    File.WriteString(File.DirDefaultExternal, "debug.txt""some text here")
    End If
    The log shows a valid file name for my temp file (and a valid TileType of 'PNG'), but still the same exception (file not found) when i try to open the temp file:

    Notice the very start of the log?
    Every time i connect the Tab2 to B4A IDE i get these Static storage paths aren't available from AID_SYSTEM error messages.
    But i don't get these error messages when i connect my S3 - only with the Tab2.

    The 'debug.txt' file is successfully created when the server creates it's second tile - that's when my variable TileId is 2.
    So using File.DirDefaultExternal, File.WriteString is working but not File.OpenOutput.

    EDIT: Just leaving the service running and spitting out lots and lots of similar exceptions i noticed this one:

    Is the problem related to the temp file creation (Bitmap to PNG to OutputStream to File) happening in a non UI Thread?

    Martin.
     
  9. thedesolatesoul

    thedesolatesoul Expert Licensed User

    Can you remove this:
    File.Delete(File.DirDefaultExternal, TempFileName)
    and try again?
    I think you are deleting the file before the http job can complete?
     
  10. warwound

    warwound Expert Licensed User

    :) Thanks - that was exactly the problem.

    Now i have another problem - how to delete the temp files my tile server will create?
    Things aren't happening in a synchronous order.
    When, after calling Response.SendFile, can i be sure that the file has been served and can now be deleted?

    I don't want the external memory to accumulate many redundant temporary files.
    IDEA! Maybe if i implement some sort of caching of created tiles i can manage all the tiles (temp files) elsewhere in the service module and avoid accumulating too many (as well as avoiding re-creating tiles that have already recently been created)...

    I'll have to think about maintaining a tile cache.

    But i think my original request is still valid.
    A ServletResponse SendBytes method would do the exact thing i need to do and avoid any asynchronous timing issues with temp files.

    Martin.
     
  11. thedesolatesoul

    thedesolatesoul Expert Licensed User

    I think if there was an event raised when the response is sent out or acknowledged? But then this is server side so I am not so sure.
    Caching sounds good but you need to still keep track of which tiles you can delete (I guess just make sure the last tile served is not deleted).
     
  12. warwound

    warwound Expert Licensed User

    Yeah the temp file solution is far from elegant, all that's needed is a new ServletResponse method:

    Code:
    public void SendBytes(byte[] Bytes){
       response=Bytes;
       res.setContentLength(response.length);
    }
    Martin.
     
    thedesolatesoul likes this.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice