F:read("*a") seems not working correctly on Windows?

I’m using io APIs to read/write external image cache for textures. It’s working well on Android but getting an error ERROR:DLIB: Failed to load image: 'Corrupt JPEG' on Windows. My guess is that the f:read("*a") doesn’t return full file content on Windows.

Here is my code:

local M = {}

local save_dir = html5 and "KyHoang_Download" or "KyHoang/Download"
local cached = {}

function M.from_external(link, callback)
	if cached[link] then
		local img = cached[link]
		gui.new_texture(link, img.width, img.height, img.type, img.buffer)
		callback(link)
	else
		local make_texture_from_local_path = function(local_path)
			local f =  io.open(local_path, "r")
			if f then
				local data = f:read("*a")
				local img = image.load(data)
				gui.new_texture(link, img.width, img.height, img.type, img.buffer)
				cached[link] = img
				callback(link)
				io.close(f)
			end
		end

		local filename = link:gsub("[%p%c%s]", "")
		local path = sys.get_save_file(save_dir, filename)

		local f = io.open(path, "r")
		if f then
			io.close(f)
			make_texture_from_local_path(path)
		else
			http.request(link, "GET", function(_self, id, response)
				if response.status == 200 or response.status == 304 then
					f = io.open(path, "w+")
					if f then
						f:write(response.response)
						io.close(f)
					end
					make_texture_from_local_path(path)
				end
			end)
		end
	end
end

return M

If you’re reading a binary file (as opposed to a text file), you have to open it with "rb" and not just "r".

Thank you, looks like that’s the cause. However the generated texture is scratched on Windows, I don’t know why. All good on Android with either “r” or “rb”

What do you mean?

I mean like this:
Screenshot 2024-06-09 234757

while the original image is:
48-1717647254

If I create texture directly from http response then it’s good but through io it’s like that

Open the cached file directly on my computer, I see the file is scratched already. So it should be something wrong when saving data with f:write(response.response)

Are you also calling f:flush() to make sure everything is written to the file?

1 Like

Yes, for binary files, you need to take care to write and read in binary format.
Otherwise white spaces may be interpreted in the wrong fashion, and you won’t get the exact same data as exists on disc.

1 Like

Thanks for your remind, the image is still scratched when having this line after f:write.

I think I should write http response.response in binary format as Mathias said but I’m confused. Isn’t it in binary format already? If it’s not then is there a function to convert it into binary fortmat?

1 Like

The response is likely binary (depending on your content-type of the resource), but when you open the file for writing, you need to open it in binary mode.

4 Likes

Thanks, now I know where to fix my code. it’s “wb+” mode

3 Likes