How Do I Slow Down Loading?

I have an HTML viewer that displays a chat for my app. When the user first comes into the chat, it loads the last 25 messages into the html viewer. However, when I load the user pictures into the html viewer, it loops through the database so quick that it doesn’t have time to display the pictures properly. It loads the messages just fine. If I slow down reading from the database by using a messagebox, all the pictures have the time to load up properly. However, I don’t want a messagebox popping up just so the pictures have time to load. I tried using update browser to force it to update but that does nothing. How can I slow down reading from the database so that it displays the pictures properly?

It would be better to store the images on the filesystem behind a traditional web server (Apache, nginx), and store URLs in the database.

1 Like

I store them in a file on the Xojo cloud. The only thing I do when reading from the database is to read the saved file name of the user. Then I load the picture from the file. Sorry. I know I made it sound like they were saved in the database. But it just reads the file name and then loads the picture from a file.

If there are repeated images, have you tried caching instances of WebPictures based on the file path? That should help the browser to reuse them.

It would be helpful to see your code.

1 Like

I haven’t read anything into caching instances of a web picture but I do think it might help in this case. How do I do that?

Anyone have any ideas?

How about encoding the pictures as base64 and place them directly into the html like this:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." alt="Beschreibung" width="200" height="150">

Or with css in the same line:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." alt="Beschreibung" style="width: 200px; height: 150px;">

If multiple pictures with the same size will be shown maybe a own css class:

<style>
    .scaled-image {
        width: 200px;
        height: 150px;
    }
</style>

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." alt="Beschreibung" class="scaled-image">

This is not optimal for large pictures, but this way you don’t need to refer to external files, let the clients browser do the work and have all pieces in place when the html is loaded.

if iVBORw0KGgoAAAANSUhEUgAA where the actual file link would be??

No, that’s the base64 encoded picture itself. It’s just the start of an example.

<img src="data:image/png;base64,Put_your_base64_encoded_picture_string_here" alt="Beschreibung" width="200" height="150">

The example is for an png picture that is base64 encoded.
Hint: When encoding add linebreaks at every, let’s say 128 characters. Otherwise you will get into trouble.

Doesn’t this sound like the regular issue of “not retaining a reference to the WebPicture” to anyone else?

1 Like

That is the only idea that I have.

2 Likes

How would i encode the image? The user uploads a picture into the program. I dont know what pictures are in their file. Is there programming to encode the picture into base 64 when they upload it so that i can store the strong into the database?

Here is the code.

Dim rs As RowSet
Dim sSQL As String
Dim sError As String
Dim j As Integer
Dim pUserPicture() As WebPicture
Dim sUserFullName() As String
Dim sUserMessage() As String
Dim sUserTime() As String

If OpenChatLogDB Then
  
  sSQL = "Select * "
  sSQL=sSQL+"FROM ChatLog "
  sSQL=sSQL+"ORDER BY dbID DESC LIMIT 20 "
  'Asc LIMIT 20"
  
  Try
    rs = dbChatLogDB.SelectSQL(sSQL)
  Catch Err As DatabaseException
    sError = "DB Error ChatLog: " + err.Message
  End Try
  
  If rs <> Nil Then
    
    If rs.RowCount > 0 Then
      
      rs.MoveToLastRow
      
      For i As Integer = 1 To rs.RowCount 'DownTo rs.RowCount -20
        pUserPicture.Add GetUserImageFromRegistration(UnScrambleString(rs.Column("dbChatPhotoName").StringValue))
        sUserFullName.Add UnScrambleString(rs.Column("dbChatFullName").StringValue)
        sUserMessage.Add UnScrambleString(rs.Column("dbChatMessage").StringValue)
        sUserTime.Add UnScrambleString(rs.Column("dbChatTime").StringValue)
        
        rs.MoveToPreviousRow
      Next
      
    Else
      sError = "RS is nil"
    End If 
    
  End If
  
  dbChatLogDB.Close
Else
  sError = "Failed to OpenChatLogDB."
End If 

For i As Integer = 0 To Ubound(sUserMessage)
  ChatViewer.AddChatBubble(sUserFullName(i) ,pUserPicture(i) ,sUserMessage(i) ,sUserTime(i))
  ChatViewer.UpdateBrowser
Next

If sError <> "" Then
  ErrorLogUpdate("Page_Chat", "", "LoadRecentChatMesssages", sError)
End If 

More code where it actually loads it into the html viewer

Public Sub AddChatBubble(extends ChatViewer as WebHTMLViewer, sName as String, pUserImage as WebPicture, sMessage as String, sTime as String = "")
  Dim sHTML As String
  Dim sJS As String
  Dim dDateTime As Date = GetLocalDateTime
  Dim sJS2 As String
  
  sjs = "document.getElementById('ChatListboxBackgroundRectangle').innerHTML +="
  sJS=sJS+ "'"
  sJS=sJS+"<div id=" + sQuoteCharacter + "ChatRow" + sQuoteCharacter + ">"
  sJS=sJS+"<img id=" + sQuoteCharacter + "UserPicture" + sQuoteCharacter + " src=" +sQuoteCharacter +  pUserImage.URL + sQuoteCharacter + "  width=50 height=50>"
  sJS=sJS+"<div id=" + sQuoteCharacter + "ChatBubble" + sQuoteCharacter + ">"
  sJS=sJS+"<span id=" + sQuoteCharacter + "UserName" + sQuoteCharacter + ">"
  sJS=sJS+sName
  sJS=sJS+"</span>"
  sJS=sJS+"<br>"
  sJS=sJS+"<div id=" + sQuoteCharacter + "UserMessage" + sQuoteCharacter+ ">"
  sJS=sJS+sMessage
  sJS=sJS+"</div>" 'UserMessage
  sJS=sJS+"</div>" 'ChatBubble
  sJS=sJS+"</div>" 'ChatRow
  sJS=sjs+"'"
  
  'Scroll down to the bottom after message is loaded
  ChatViewer.ExecuteJavaScript(sJS)
  sJS="let divElement = document.getElementById('ChatListboxBackgroundRectangle');"
  sjs=sJS+ " divElement.scrollTop = divElement.scrollHeight;"
  
  ChatViewer.ExecuteJavaScript(sJS)
  
  '
End Sub

You need to do as both @Ricardo_Cruz and @Tim_Parnell suggested. Save a reference to your WebPicture instances. If you don’t, then they go out of scope and a 404 is returned when the URL is accessed in the client browser. You can do this in an array on the page/dialog/whatever, if nothing else. Encoding them as BASE64 and embedding that data is a bit overkill.

1 Like

For small pictures it should do. As I said this way there is no need for external files to be loaded.
Here an example to encode the picturedata:

Function EncodeImageToBase64(filePath As String) As String
  
  Try
    // Load Picture from file
    Var f As FolderItem = New FolderItem(filePath, FolderItem.PathModes.Native)
    If f = Nil Or Not f.Exists Then
      Return ""
    End If
    
    Var inputStream As BinaryStream = BinaryStream.Open(f, False)
    Var imageData As MemoryBlock = inputStream.Read(inputStream.Length)
    inputStream.Close
    
    // Base64 
    Var base64String As String = EncodeBase64(imageData, 128)
       
    Return base64String
  
  Catch e As RuntimeException
    Return ""
  End Try
  
End Function

Im not sure what this means to save a reference to the instances of webPicture. Im saving then in the method in a picture array. Do you mean to create an picture array at the page level and save them there?

Yep that did the job. Now the pictures are loading up as they should. Thanks for the help.