Working with RARs in ColdFusion - Why I did it...

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")>

Raymond Camden's Picture

About Raymond Camden

Raymond is a developer advocate. He focuses on JavaScript, serverless and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Comments