A few months ago I wrote a blog post about working with RAR files in ColdFusion. The short story is that RAR, a file archive format, is not supported by cfzip. Surprisingly there doesn't seem to be in Java packages out there that provide full integration with the format. I ended up writing a wrapper around the free 7-Zip command line program. I mentioned this in passing but the real reason I wanted to build this was to do something with the CBR files (Comic Book files) I had on my hard drive. I thought it would be interesting to write code to extract the cover image from the files. You could imagine many uses for this. Perhaps simply creating a nice HTML page of covers would be neat. Whatever. To be honest, it's kind of pointless - but that hasn't stopped me before so why now. Here is what I built.

I began by creating a list of comics from my hard drive. I assumed I was going to have multiple screw ups as I worked on my code so I also added basic caching in.

<cfset dir = "H:\comics"> <cfset comics = cacheGet("comics")> <cfif isNull(comics)> <cfoutput> <p>Getting initial list...</p> </cfoutput> <cfset comics = directoryList(dir,true,"list","*.cbr")> <cfset cachePut("comics", comics,createTimeSpan(0,0,10,0))> </cfif>

<!--- make a copy since arraydelat will change copy ---> <cfset comics = duplicate(comics)>

That last bit of code there just handles breaking the reference to the cache since I end up manipulating the results a bit. Ok - now a quick output:

<cfoutput> <p>There are #arrayLen(comics)# total comics.</p> </cfoutput>

I then decided to grab 100 comics from the set:

<!--- Given our list, pick a set ---> <cfset chosen = []> <cfset total = min(100, arrayLen(comics))> <cfloop condition="arrayLen(chosen) lt total"> <cfset target = randRange(1, arrayLen(comics))> <!--- I had a few ._ files ---> <cfif not find("\._",comics[target])> <cfset arrayAppend(chosen, comics[target])> <cfset arrayDeleteAt(comics, target)> </cfif> </cfloop>

And just to be sure - output that size as well:

<cfoutput> <p>Ok, we have #arrayLen(chosen)# comics.</p> </cfoutput>

Next - I set up a temporary directory. Remember that I'm going to be shelling out to an executable. This means I won't be able to use RAM drives:

<!--- make a temp dir ---> <cfset tmpDir = expandPath("./comics")> <cfif not directoryExists(tmpDir)> <cfdirectory action="create" directory="#tmpDir#"> </cfif>

Ok - now let's make our CFC:

<!--- initialize our CFC ---> <cfset sevenZipexe = "C:\Program Files\7-Zip\7z.exe"> <cfset sevenzipcfc = new sevenzip(sevenzipexe)>

Now we are going to loop through our comics and try to get the cover. In general, the first item in a list of files from a CBR RAR file is either an image, or a directory. So my code handles that and looks in the second item if the first is a directory:

<!--- now get our covers ---> <cfloop index="c" array="#chosen#"> <cftry> <cfset files = sevenzipcfc.list(c)> <!--- first item may be a directory - for now we see if size is 0, and if 0, skip to the next ---> <cfif files.size[1] neq 0 and listLast(files.name[1],".") is "jpg"> <cfset sevenzipcfc.extract(c,files.name[1],tmpdir)> <cfoutput>Just extracted #files.name[1]# from #c#<br/></cfoutput> <cfelseif files.recordCount gte 2 and files.size[2] neq 0 and listLast(files.name[2],".") is "jpg"> <cfset sevenzipcfc.extract(c,files.name[2],tmpdir)> <cfoutput>Just extracted #files.name[2]# from #c#<br/></cfoutput> </cfif> <cfcatch> <cfoutput><b>Error: #cfcatch.message#</b><br/></cfoutput> </cfcatch> </cftry> </cfloop>

At this point I had a bunch of images! It worked. Then I thought - let's do something neat with the images...

<cfset images = directoryList(tmpdir)> <cfset canvas = imageNew("", 1500, 1145)> <cfset processed = 0> <cfset i = 1> <cfset x = 0> <cfset y = 0> <cfloop condition="processed lt 50"> <cfif isImageFile(images[i])> <cfset myimg = imageRead(images[i])> <cfset imageResize(myimg,150,229)> <!--- Position is based on the index ---> <cfset imagePaste(canvas, myimg, x, y)> <cfset processed++> <cfset x+= 150> <cfif x is 1500> <cfset x = 0> <cfset y+= 229> </cfif> </cfif> <cfset i++> </cfloop>

Basically - read in a list of images, process line by line and resize each one. I then paste it into a large canvas moving from left to right. And finally...

<cfset imageWrite(canvas, "c:\Users\Raymond\Desktop\finalcover.jpg")>

So the result? Click to make it bigger...

Not perfect - but kind of cool I think. Here is the entire template. Please note I wrote this rather quickly. It's not meant to be production ready.

<cfset dir = "H:\comics"> <cfset comics = cacheGet("comics")> <cfif isNull(comics) or 0> <cfoutput> <p>Getting initial list...</p> </cfoutput> <cfset comics = directoryList(dir,true,"list","*.cbr")> <cfset cachePut("comics", comics,createTimeSpan(0,0,10,0))> </cfif>

<!--- make a copy since arraydelat will change copy ---> <cfset comics = duplicate(comics)>

<cfoutput> <p>There are #arrayLen(comics)# total comics.</p> </cfoutput>

<!--- Given our list, pick a set ---> <cfset chosen = []> <cfset total = min(100, arrayLen(comics))> <cfloop condition="arrayLen(chosen) lt total"> <cfset target = randRange(1, arrayLen(comics))> <!--- I had a few ._ files ---> <cfif not find("._",comics[target])> <cfset arrayAppend(chosen, comics[target])> <cfset arrayDeleteAt(comics, target)> </cfif> </cfloop>

<cfoutput> <p>Ok, we have #arrayLen(chosen)# comics.</p> </cfoutput>

<!--- make a temp dir ---> <cfset tmpDir = expandPath("./comics")> <cfif not directoryExists(tmpDir)> <cfdirectory action="create" directory="#tmpDir#"> </cfif>

<!--- initialize our CFC ---> <cfset sevenZipexe = "C:\Program Files\7-Zip\7z.exe"> <cfset sevenzipcfc = new sevenzip(sevenzipexe)>

<!--- now get our covers ---> <cfloop index="c" array="#chosen#"> <cftry> <cfset files = sevenzipcfc.list(c)> <!--- first item may be a directory - for now we see if size is 0, and if 0, skip to the next ---> <cfif files.size[1] neq 0 and listLast(files.name[1],".") is "jpg"> <cfset sevenzipcfc.extract(c,files.name[1],tmpdir)> <cfoutput>Just extracted #files.name[1]# from #c#<br/></cfoutput> <cfelseif files.recordCount gte 2 and files.size[2] neq 0 and listLast(files.name[2],".") is "jpg"> <cfset sevenzipcfc.extract(c,files.name[2],tmpdir)> <cfoutput>Just extracted #files.name[2]# from #c#<br/></cfoutput> </cfif> <cfcatch> <cfoutput><b>Error: #cfcatch.message#</b><br/></cfoutput> </cfcatch> </cftry> </cfloop>

<cfset images = directoryList(tmpdir)> <cfset canvas = imageNew("", 1500, 1145)> <cfset processed = 0> <cfset i = 1> <cfset x = 0> <cfset y = 0> <cfloop condition="processed lt 50"> <cfif isImageFile(images[i])> <cfset myimg = imageRead(images[i])> <cfset imageResize(myimg,150,229)> <!--- Position is based on the index ---> <cfset imagePaste(canvas, myimg, x, y)> <cfset processed++> <cfset x+= 150> <cfif x is 1500> <cfset x = 0> <cfset y+= 229> </cfif> </cfif> <cfset i++> </cfloop>

<cfset imageWrite(canvas, "c:\Users\Raymond\Desktop\finalcover.jpg")>