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.
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.
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?
That is the only idea that I have.
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.
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.