compose-imageloader
compose-imageloader copied to clipboard
Loading images in browser MissingAllowOriginHeader issue
Hi,
Hi, I am building a compose app that should load images from a 3rd party website in a grid. I am using the 1.7.2-SNAPSHOT from https://github.com/qdsfdhvh/compose-imageloader/issues/151. It works as expected for all native platforms but in the browser, I get this error: Cors-Origin Resource Sharing error: MissingAllowOriginHeader
.
When sending the image request, the browser adds an Origin and a Referer header. This seems relevant because the browser app I'm rewriting, doesn't send these two headers and images load correctly.
I think setting mode no-cors
in the fetch API should solve the issue but I don't know how to do this using imageloader's API. Any help is appreciated.
Thanks!
florin
Currently, You can customise the ktorClient:
val imageLoader = ImageLoader {
// ...
components {
setupDefaultComponents(
httpClient = {
HttpClient(/* OkHttp.create() or other */) {
// some ktor config
}
}
)
}
}
If you need to pass some parameters from the ImageRequest
, you need to customise the ktor fetcher with the help of the Options.extra
:
val imageRequest = ImageRequest {
// ...
options {
extra {
put("headerMap", mapOf("key1" to "value1"))
}
}
}
// ...
import io.ktor.http.Url
class CustomKtorUrlFetcher(
private val httpUrl: Url,
private val headerMap: Map<String, String>,
httpClientFactory: () -> HttpClient,
) : Fetcher {
private val httpClient by lazy(httpClientFactory)
override suspend fun fetch(): FetchResult {
val response = httpClient.request {
url(httpUrl)
headers {
headerMap.forEach { (key, value) ->
append(key, value)
}
}
}
if (!response.status.isSuccess()) {
error("code:${response.status.value}, ${response.status.description}")
}
return FetchResult.OfSource(
source = Buffer().apply {
write(response.bodyAsChannel().toByteArray())
},
extra = extraData {
mimeType(response.contentType()?.toString())
},
)
}
class Factory(private val httpClientFactory: () -> HttpClient) : Fetcher.Factory {
override fun create(data: Any, options: Options): Fetcher? {
if (data !is Url) return null
val headerMap = options.extra["headerMap"] as Map<String, String>
return CustomKtorUrlFetcher(
httpUrl = data,
headerMap = headerMap,
httpClientFactory = httpClientFactory,
)
}
}
}
// Use
val imageLoader = ImageLoader {
// ...
components {
add(CustomKtorUrlFetcher.Factory(
httpClientFactory = {
HttpClient(/* OkHttp.create() or other */) {
// some ktor config
}
}
))
setupDefaultComponents()
}
}
@florind were you able to make any more progress with this?
I ended up creating an image proxy:
wf_client = httpx.AsyncClient(base_url="base-url-for-images")
@app.get("{path:path}.png")
async def serve_image(path: str):
url = httpx.URL(path=f"{path}.png")
print(url)
rp_req = wf_client.build_request("GET", url)
rp_resp = await wf_client.send(rp_req, stream=True)
return StreamingResponse(
rp_resp.aiter_raw(),
status_code=rp_resp.status_code,
headers=rp_resp.headers,
background=BackgroundTask(rp_resp.aclose),
)