Closed alekseyHunter closed 1 month ago
What's the exception stacktrace from AsyncImage
's onError
?
@colinrtwhite The exception stacktrace is the following:
java.nio.file.InvalidPathException: Illegal char <:> at index 2: /H:/1.png
at java.base/sun.nio.fs.WindowsPathParser.normalize(WindowsPathParser.java:182)
at java.base/sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:153)
at java.base/sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:77)
at java.base/sun.nio.fs.WindowsPath.parse(WindowsPath.java:92)
at java.base/sun.nio.fs.WindowsFileSystem.getPath(WindowsFileSystem.java:232)
at java.base/java.nio.file.Path.of(Path.java:147)
at java.base/java.nio.file.Paths.get(Paths.java:69)
at okio.Path.toNioPath(Path.kt:102)
at okio.NioSystemFileSystem.metadataOrNull(NioSystemFileSystem.kt:35)
at okio.internal.-FileSystem.commonMetadata(FileSystem.kt:36)
at okio.FileSystem.metadata(FileSystem.kt:33)
at coil3.key.FileUriKeyer.key(FileUriKeyer.kt:16)
at coil3.key.FileUriKeyer.key(FileUriKeyer.kt:8)
at coil3.ComponentRegistry.key(ComponentRegistry.kt:56)
at coil3.memory.MemoryCacheService.newCacheKey(MemoryCacheService.kt:45)
at coil3.intercept.EngineInterceptor.intercept(EngineInterceptor.kt:52)
at coil3.intercept.RealInterceptorChain.proceed(RealInterceptorChain.kt:31)
at coil3.RealImageLoader$executeMain$result$1.invokeSuspend(RealImageLoader.common.kt:136)
at coil3.RealImageLoader$executeMain$result$1.invoke(RealImageLoader.common.kt)
at coil3.RealImageLoader$executeMain$result$1.invoke(RealImageLoader.common.kt)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:65)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:167)
at kotlinx.coroutines.BuildersKt.withContext(Unknown Source)
at coil3.RealImageLoader.executeMain(RealImageLoader.common.kt:127)
at coil3.RealImageLoader.access$executeMain(RealImageLoader.common.kt:42)
at coil3.RealImageLoader$executeMain$1.invokeSuspend(RealImageLoader.common.kt)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:235)
at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined(DispatchedTask.kt:191)
at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:163)
at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:474)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:508)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:497)
at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:368)
at kotlinx.coroutines.flow.StateFlowSlot.makePending(StateFlow.kt:284)
at kotlinx.coroutines.flow.StateFlowImpl.updateState(StateFlow.kt:349)
at kotlinx.coroutines.flow.StateFlowImpl.setValue(StateFlow.kt:316)
at coil3.compose.ConstraintsSizeResolver.measure-3p2s80s(ConstraintsSizeResolver.kt:32)
at androidx.compose.ui.node.BackwardsCompatNode.measure-3p2s80s(BackwardsCompatNode.kt:311)
at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
at androidx.compose.foundation.layout.FillNode.measure-3p2s80s(Size.kt:698)
at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasure$2.invoke(LayoutNodeLayoutDelegate.kt:1499)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasure$2.invoke(LayoutNodeLayoutDelegate.kt:1495)
at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2300)
at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:471)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:234)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui(OwnerSnapshotObserver.kt:133)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui(OwnerSnapshotObserver.kt:113)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1495)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:35)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:560)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.measure-BRTryo0(LayoutNodeLayoutDelegate.kt:539)
at androidx.compose.foundation.layout.BoxKt$boxMeasurePolicy$1.measure-3p2s80s(Box.kt:114)
at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:126)
at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s(GraphicsLayerModifier.kt:646)
at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
at androidx.compose.foundation.layout.SizeNode.measure-3p2s80s(Size.kt:837)
at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
at androidx.compose.foundation.layout.FillNode.measure-3p2s80s(Size.kt:698)
at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
at androidx.compose.foundation.layout.PaddingNode.measure-3p2s80s(Padding.kt:397)
at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasure$2.invoke(LayoutNodeLayoutDelegate.kt:1499)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasure$2.invoke(LayoutNodeLayoutDelegate.kt:1495)
at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2300)
at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:471)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:234)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui(OwnerSnapshotObserver.kt:133)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui(OwnerSnapshotObserver.kt:113)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1495)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:35)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:560)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.measure-BRTryo0(LayoutNodeLayoutDelegate.kt:539)
at androidx.compose.foundation.layout.RowColumnMeasurementHelper.measureWithoutPlacing-_EkL_-Y(RowColumnMeasurementHelper.kt:112)
at androidx.compose.foundation.layout.RowColumnImplKt$rowColumnMeasurePolicy$1.measure-3p2s80s(RowColumnImpl.kt:72)
at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:126)
at androidx.compose.foundation.layout.PaddingNode.measure-3p2s80s(Padding.kt:397)
at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasure$2.invoke(LayoutNodeLayoutDelegate.kt:1499)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasure$2.invoke(LayoutNodeLayoutDelegate.kt:1495)
at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2300)
at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:471)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:234)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui(OwnerSnapshotObserver.kt:133)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui(OwnerSnapshotObserver.kt:113)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1495)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:35)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:560)
at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui(LayoutNode.kt:1140)
at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui$default(LayoutNode.kt:1131)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.doRemeasure-sdFAvZA(MeasureAndLayoutDelegate.kt:323)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:458)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.access$remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:39)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureAndLayout(MeasureAndLayoutDelegate.kt:344)
at androidx.compose.ui.platform.SkiaBasedOwner.measureAndLayout(SkiaBasedOwner.skiko.kt:246)
at androidx.compose.ui.node.Owner.measureAndLayout$default(Owner.kt:223)
at androidx.compose.ui.ComposeScene.render(ComposeScene.skiko.kt:546)
at androidx.compose.ui.awt.ComposeBridge$skikoView$1$onRender$1.invoke(ComposeBridge.desktop.kt:178)
at androidx.compose.ui.awt.ComposeBridge$skikoView$1$onRender$1.invoke(ComposeBridge.desktop.kt:177)
at androidx.compose.ui.awt.ComposeBridge.catchExceptions(ComposeBridge.desktop.kt:150)
at androidx.compose.ui.awt.ComposeBridge.access$catchExceptions(ComposeBridge.desktop.kt:64)
at androidx.compose.ui.awt.ComposeBridge$skikoView$1.onRender(ComposeBridge.desktop.kt:177)
at org.jetbrains.skiko.SkiaLayer.update$skiko(SkiaLayer.awt.kt:548)
at org.jetbrains.skiko.redrawer.AWTRedrawer.update(AWTRedrawer.kt:54)
at org.jetbrains.skiko.redrawer.Direct3DRedrawer$frameDispatcher$1.invokeSuspend(Direct3DRedrawer.kt:49)
at org.jetbrains.skiko.redrawer.Direct3DRedrawer$frameDispatcher$1.invoke(Direct3DRedrawer.kt)
at org.jetbrains.skiko.redrawer.Direct3DRedrawer$frameDispatcher$1.invoke(Direct3DRedrawer.kt)
at org.jetbrains.skiko.FrameDispatcher$job$1.invokeSuspend(FrameDispatcher.kt:33)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
@colinrtwhite After updating to version 3.0.0-alpha02
and changing the file path from "file:///H:/1.png"
to "H://1.png"
the exception stacktrace looks like this:
java.lang.IllegalStateException: Unable to create a fetcher that supports: H:\1.png
at coil3.intercept.EngineInterceptor.fetch(EngineInterceptor.kt:145)
at coil3.intercept.EngineInterceptor.execute(EngineInterceptor.kt:109)
at coil3.intercept.EngineInterceptor.access$execute(EngineInterceptor.kt:29)
at coil3.intercept.EngineInterceptor$intercept$2.invokeSuspend(EngineInterceptor.kt:63)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:103)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:589)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:806)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:710)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:697)
Looks like this has nothing to do with the coil library. /H:
is not a valid directory path. Remove the colon :
after the H path.
@devmike01 This doesn't work, I got another exception - FileNotFoundException
;)
java.io.FileNotFoundException: no such file: /H/1.png
at okio.internal.-FileSystem.commonMetadata(FileSystem.kt:36)
at okio.FileSystem.metadata(FileSystem.kt:33)
at coil3.key.FileUriKeyer.key(FileUriKeyer.kt:16)
at coil3.key.FileUriKeyer.key(FileUriKeyer.kt:8)
at coil3.ComponentRegistry.key(ComponentRegistry.kt:66)
@alekseyHunter I agree with @devmike01 I think there might be something wrong with your path, though I'm not sure exactly how to represent a Windows drive name in an Okio path. I'd try: file://\H:\\1.png
.
There might also be issues with Coil relying on /
as Windows uses \
for file paths.
@colinrtwhite This doesn't work either (file://\H:\\1.png
). I caught the same exception:
java.lang.IllegalStateException: Unable to create a fetcher that supports: file://\H:\\1.png
at coil3.intercept.EngineInterceptor.fetch(EngineInterceptor.kt:145)
at coil3.intercept.EngineInterceptor.execute(EngineInterceptor.kt:109)
at coil3.intercept.EngineInterceptor.access$execute(EngineInterceptor.kt:29)
at coil3.intercept.EngineInterceptor$intercept$2.invokeSuspend(EngineInterceptor.kt:63)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:103)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:589)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:806)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:710)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:697)
@alekseyHunter Fix your error or post the issue on StackOverflow. Try accessing this image(file://\H:\\1.png
) file from your browser and see if you are able to do so. I doubt it. Again, this has nothing to do with Coil.
Fix your error or post the issue on StackOverflow.
@devmike01 What's the logic? How to fix the error, if there is an error in the library.
I doubt it. Again, this has nothing to do with Coil.
@devmike01 Can you load a local picture on your computer using the library? Will you show the result on video?
Try accessing this image(file://\H:\1.png) file from your browser and see if you are able to do so. I doubt it.
@devmike01 Everything loads fine.
I meant web browser, not your image browser.
@devmike01 Yes, the second screenshot shows the result in the web browser (pixlr.com). Everything works in it ;)
The second images is hosted on a server. I meant load the photo from your folder path in your web browser.
I just had the same issue, it's nice to know that it wasn't me. I'm using 3.0.0-Alpha 04.
When passing in a File
, Coil fails to load the image on Jetbrains Compose (JVM) on Windows.
I had to do this to get it to work:
val loc = "file://" + imgFiles[1].absolutePath.replace("\\", "/")
AsyncImage(
model = loc, contentDescription = "Test Photo"
)
I would assume that we should expect to just pass a File object to Coil, and it would work correctly, right?
It definitely does not seem to like Windows file paths.
While the above temporary fix does work on my C drive, it also does not work when referencing a file on another drive.
I run into the exact same problem. I am on 3.0.0-alpha04
implementing a KMP project targeting Windows and Android.
The file I want to show on Windows is located at "C:\Users\<user>\AppData\Roaming\<project>\Thumbnails\tool2019fearinoculum.jpg"
(c+p from file explorer)
When I run
File("C:/Users/<user>/AppData/Roaming/<project>/Thumbnails/tool2019fearinoculum.jpg").exists()
or
File("C:\\Users\\<user>\\AppData\\Roaming\\<project>\\Thumbnails\\tool2019fearinoculum.jpg").exists()
it returns true
. However, when I run one of the above like this
AsyncImage(
model = File("C:/Users/<user>/AppData/Roaming/<project>/Thumbnails/tool2019fearinoculum.jpg"),
contentDescription = "null",
onError = { println(it) }
)
it gives me throwable=java.lang.IllegalStateException: Unable to create a fetcher that supports: file://C:\Users\<user>\AppData\Roaming\<project>\Thumbnails\tool2019fearinoculum.jpg
.
I agree with @ejektaflex. If I am not doing anything completely wrong, I would also assume this code to work.
Edit:
Putting file://C:\Users\<user>\AppData\Roaming\<project>\Thumbnails\tool2019fearinoculum.jpg
into the browser shows the image. Passing it as the model parameter, however, does not work.
Same here. Compose for Desktop in a Windows 11 machine.
AsyncImage(
model = imageFile,
contentDescription = "test",
contentScale = ContentScale.Fit,
modifier = Modifier.fillMaxSize(),
onError = { println(it.result.throwable)}
)
This shows nothing in my UI, but outputs the following error:
java.lang.IllegalStateException: Unable to create a fetcher that supports: file://.myapp\190f6e6c-e527-456a-84bc-301a62bf5486.png
I'm positive the file is OK, because I'm scanning a folder, and I'm migrating from another image loader that displayed it with no problem. Also, if I pass an URL of a remote image, it loads and displays perfectly fine.
This workaround is fine in my Windows, but I suppose it will break anywhere else.
val loc = "file://" + imageFile.absolutePath.replace("\\", "/")
This workaround is fine in my Windows, but I suppose it will break anywhere else.
val loc = "file://" + imageFile.absolutePath.replace("\\", "/")
Would you mind posting the exact contents of the String that you passed to AsyncImage? My images are located in C:...\AppData\ and I tried all possibilities with \
and /
and nothing would work for me...
This workaround is fine in my Windows, but I suppose it will break anywhere else.
val loc = "file://" + imageFile.absolutePath.replace("\\", "/")
Would you mind posting the exact contents of the String that you passed to AsyncImage? My images are located in C:...\AppData\ and I tried all possibilities with
\
and/
and nothing would work for me...
Of course, this is the original file: .myapp\190f6e6c-e527-456a-84bc-301a62bf5486.png
(I've tried passing both the File and its path as a String) and the workaround is using the String file://.myapp/190f6e6c-e527-456a-84bc-301a62bf5486.png
instead.
This workaround is fine in my Windows, but I suppose it will break anywhere else.
val loc = "file://" + imageFile.absolutePath.replace("\\", "/")
Would you mind posting the exact contents of the String that you passed to AsyncImage? My images are located in C:...\AppData\ and I tried all possibilities with
\
and/
and nothing would work for me...Of course, this is the original file:
.myapp\190f6e6c-e527-456a-84bc-301a62bf5486.png
(I've tried passing both the File and its path as a String) and the workaround is using the Stringfile://.myapp/190f6e6c-e527-456a-84bc-301a62bf5486.png
instead.
Thank you. What exactly is .myapp? Is it the root directory of your app? I was looking for a workaround where I can load images from the "AppData" folder on the C: drive. Any ideas? Because using the absolute path with the drive letter seems to create the confusion within Coil.
I also tried it with Okio itself:
FileSystem.SYSTEM.exists("C:/Users/<user>/AppData/Roaming/<project>/Thumbnails/tool2019fearinoculum.jpg".toPath())
returns true
. If I put this Okio path into the AsyncImage like this
AsyncImage(
model = "C:/Users/<user>/AppData/Roaming/<project>/Thumbnails/tool2019fearinoculum.jpg".toPath(),
contentDescription = "null",
onError = { println(it) }
)
it throws java.io.FileNotFoundException: no such file: /Users/<user>/AppData/Roaming/<project>/Thumbnails/tool2019fearinoculum.jpg
. It seems to swallow the drive letter thus making the path invalid.
This workaround is fine in my Windows, but I suppose it will break anywhere else.
val loc = "file://" + imageFile.absolutePath.replace("\\", "/")
Would you mind posting the exact contents of the String that you passed to AsyncImage? My images are located in C:...\AppData\ and I tried all possibilities with
\
and/
and nothing would work for me...Of course, this is the original file:
.myapp\190f6e6c-e527-456a-84bc-301a62bf5486.png
(I've tried passing both the File and its path as a String) and the workaround is using the Stringfile://.myapp/190f6e6c-e527-456a-84bc-301a62bf5486.png
instead.Thank you. What exactly is .myapp? Is it the root directory of your app? I was looking for a workaround where I can load images from the "AppData" folder on the C: drive. Any ideas? Because using the absolute path with the drive letter seems to create the confusion within Coil.
It's just a subfolder of the working directory of my app, nothing special.
I have the same issue on Windows. Using workaround below works on C drive only, not on other drive letters.
val loc = "file://" + imageFile.absolutePath.replace("\\", "/")
I think the issue is in FileUriFetcher.kt in Coil.
It has this code:
override suspend fun fetch(): FetchResult {
val path = checkNotNull(uri.path) { "path == null" }.toPath()
return SourceFetchResult(
source = ImageSource(path, options.fileSystem),
mimeType = MimeTypeMap.getMimeTypeFromExtension(path.extension),
dataSource = DataSource.DISK,
)
}
For example input file on drive D "file://D:/861746.jpg" the resulting path in this code becomes "/861768.jpg", thus the files location cannot be resolved.
It works for C drive, because that is the default drive.
The solution is the above code should take drive letter into consideration.
EDIT: I think it might even work with normal windows style path like D:\861768.jp looking and OKIO toPath code, but then the issue is the Coil ComponentRegistry never gets to use the FileUriFetcher..
Possible workaround to use normal windows path.
Create a custom fetcher:
internal class WindowsFileUriFetcher(
private val uri: Uri,
private val options: Options,
) : Fetcher {
@OptIn(InternalCoilApi::class)
override suspend fun fetch(): FetchResult {
val path = uri.toString().toPath()
return SourceFetchResult(
source = ImageSource(path, options.fileSystem),
mimeType = MimeTypeMap.getMimeTypeFromExtension(path.name.substringAfterLast('.', "")),
dataSource = DataSource.DISK,
)
}
class Factory : Fetcher.Factory<Uri> {
private val regex = "^[a-zA-Z]:\\\\.*".toRegex()
override fun create(
data: Uri,
options: Options,
imageLoader: ImageLoader,
): Fetcher? {
if (hostOs != OS.Windows || !regex.matches(data.toString())) return null
return WindowsFileUriFetcher(data, options)
}
}
}
Register it:
ImageLoader.Builder(context)
.components {
add(OkHttpNetworkFetcherFactory())
add(WindowsFileUriFetcher.Factory())
}
.build()
Is this still happening? Do you need any help creating a patch here?
Describe the bug Hello! Local image files don't show on PC. Error type is not in the function
error()
To Reproduce
Logs/Screenshots
Version Coil 3.0.0-alpha01
kotlin.version=1.9.21 agp.version=8.1.4 compose.version=1.5.11