Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

Monday, August 28, 2023

Caused by java.io.IOException: CreateProcess error=206, The filename or extension is too long

While working on Java gradle project sometimes we might get the following error in Windows system:

Caused by: java.io.IOException: CreateProcess error=206, The filename or extension is too long

This is due to when classpath for a Gradle JavaExec or Test task is long, Windows command executions give error because of limitation to command line length greater than 32K

With a number of classpath dependencies in a large project, typically JavaExec Gradle task fails with error "The filename or extension is too long" and this would be a stopping error. To solve this issue, we can use the following gradle plugin. Use the plugin inside build.gradle file

apply plugin: "com.virgo47.ClasspathJar"

Or we can use inside build.gradle file under buildscript {} >> dependencies {} section as below:

buildscript {
  repositories {
    maven {
      url "https://plugins.gradle.org/m2/"
    }
  }
  dependencies {
    classpath "gradle.plugin.com.virgo47.gradle:ClasspathJar:1.0.0"
  }
}

For Kotlin project, use it inside build.gradle.kts file as below:

apply(plugin = "com.virgo47.ClasspathJar")

Or we can use it in the build.gradle.kts kotlin file under buildscript {} >> dependencies {}

buildscript {
  repositories {
    maven {
      url = uri("https://plugins.gradle.org/m2/")
    }
  }
  dependencies {
    classpath("gradle.plugin.com.virgo47.gradle:ClasspathJar:1.0.0")
  }
}

ClasspathJar plugin creates a classpath jar for jar files in the classpath and sets the classpath with classpath jar. This includes JavaExec, Test and other similar tasks.

Build the application and run it.

Reference: ClasspathJar

Share:

Tuesday, August 23, 2022

Mail Sending Issue in Java Application

Sometimes while sending the mail in the java application we might get the following issue.

org.springframework.mail.MailSendException: Mail server connection failed; nested exception is com.sun.mail.util.MailConnectException: Couldn't connect to host, port: smtp.gmail.com, 465; timeout -1;
  nested exception is:
	java.net.ConnectException: Connection timed out (Connection timed out). Failed messages: com.sun.mail.util.MailConnectException: Couldn't connect to host, port: smtp.gmail.com, 465; timeout -1;
  nested exception is:
	java.net.ConnectException: Connection timed out (Connection timed out)
	at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:432)
	at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:345)
	at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:340)
	at org.springframework.mail.javamail.JavaMailSender$send$0.call(Unknown Source)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
	at grails.plugins.mail.MailMessageBuilder.sendMessage(MailMessageBuilder.groovy:130)
	Caused by: com.sun.mail.util.MailConnectException: Couldn't connect to host, port: smtp.gmail.com, 465; timeout -1
Caused by: java.net.ConnectException: Connection timed out (Connection timed out)

This might be due to a different reason.

Due to Firewall setup:

While sending mail we are using ports like 465 or 587 or other ports. So if you enable the firewall rules on your server then we might get this issue. So, enable the firewall rules for those TCP ports for outbound in your firewall settings.

The service provider disabled the port:

For security reasons, some server service providers disable those ports. For e.g:

For this, contact the service provider to allow the port or change the port on your configuration for your application. In the java application, for Gmail smtp.gmail.com we can use 587 instead of 465. The SMTP configuration looks as below:

["mail.smtp.host"           : "smtp.gmail.com",
				 "mail.smtp.starttls.enable": "true",
				 "mail.smtp.auth"           : "true",
				 "mail.smtp.port"           : "587",
		]
Share:

Tuesday, August 16, 2022

Convert Date to Pretty Time in Grails and Groovy

In this tutorial, we will learn how to convert Java Date to pretty time like moments ago, 1 hour ago, 1 week ago, 1 month ago, 1 year ago, and so on in grails application.

For this, we are using the prettytime plugin in our project.

Load PrettyTime in Grails Gradle project:

Add the following inside dependencies in the build.gradle file.

dependencies {
//other dependencies
 
compile 'org.grails.plugins:grails-pretty-time:4.0.0'
}

PrettyTime format Date:

Now let's create a method that formats the Java Date

import org.ocpsoft.prettytime.PrettyTime
import java.util.Date
public static String formatPrettyTime(Date date) {
        PrettyTime p = new PrettyTime()
        return p.format(date).trim()
    }

This will format the given date to a pretty time like moments ago.

Pretty Time Support Locale:

Prettytime support different language, for this use request to get the current locale and format it.

public static String formatPrettyTime(Date date, request) {
        Locale locale = RequestContextUtils.getLocale(request)
        PrettyTime prettyTime = new PrettyTime(locale)
        return prettyTime.format(date).trim()
    }

Here, we are using the locale from the request which gives the session locale for the current user

Current locale in grails application can also be achieved using LocaleContextHolder

import org.springframework.context.i18n.LocaleContextHolder
Locale locale = LocaleContextHolder.getLocale()

For pretty time supported language please follow prettyTime

Use in Gsp page:

If we are using the GSP pages HTML as server-side rendering, then we can use pretty time in GSP pages as below

<prettytime:display date="${someDate}" />
Share:

Monday, August 15, 2022

Convert Java Date to Pretty Time in Java

In this tutorial, we will learn how to convert Java Date to pretty time like moments ago, 1 hour ago, 1 week ago, 1 month ago, 1 year ago, and so on.

For this, we are using the prettytime dependency in our project.

If we are using a jar file download the desired jar file from the maven repository and load the jar file in the application. Please follow How to add external jar or library on IntelliJ IDEA project

Loading PrettyTime in Gradle Project:

Add the following inside dependencies in the build.gradle file.

dependencies {
//other dependencies
 
implementation 'org.ocpsoft.prettytime:prettytime:5.0.3.Final'
}

Loading PrettyTime in Maven Project:

Add the following dependency inside the pom.xml file.

<dependency>
  <groupId>org.ocpsoft.prettytime</groupId>
  <artifactId>prettytime</artifactId>
  <version>5.0.3.Final</version>
  <type>bundle</type>
</dependency>

Pretty Time format Date:

Now let's create a sample java class PrettyDateTime.java and create a converter method.

import org.ocpsoft.prettytime.PrettyTime;

import java.util.Date;

public class PrettyDateTime {

    public static void main(String[] args) {
        Date dateToConvert = new Date();
        System.out.println(convert(dateToConvert));
    }

    private static String convert(Date date) {
        PrettyTime p = new PrettyTime();
        return p.format(date);
    }
}

Here, we are creating the method convert which will convert the given Java Date to pretty time e.g moments ago.

Pretty Time format LocalDateTime:

import org.ocpsoft.prettytime.PrettyTime;

import java.time.LocalDateTime;

public class PrettyDateTime {

    public static void main(String[] args) {
        System.out.println(convert(LocalDateTime.now().minusSeconds(864000)));
    }

    private static String convert(LocalDateTime date) {
        PrettyTime p = new PrettyTime();
        return p.format(date);
    }
}

Pretty Time format in multiple languages:

Pretty time supports i18n and multiple languages.

import org.ocpsoft.prettytime.PrettyTime;

import java.util.Date;
import java.util.Locale;

public class PrettyDateTime {

    public static void main(String[] args) {
        Date dateToConvert = new Date();
        System.out.println(convert(dateToConvert, new Locale("de")));
    }

    private static String convert(Date date, Locale locale) {
        PrettyTime p = new PrettyTime(locale);
        return p.format(date);
    }
}

Here, we are using german support with "de" locale. For available language support follow prettyTime

Share:

Thursday, August 11, 2022

Java format date string to date and vice versa

This is a quick tutorial on formatting date in the string to java Date and Date to a date in string.

Let's create a sample java class called DateTimeUtils.java.

Parse Date String to Date:

Let's look into the example that we want to parse the date string 2022-08-22 or 2022-08-22 04:22:100 to Date.

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateTimeUtils {

    public static void main(String[] args) throws ParseException {
        String format = "yyyy-MM-dd HH:mm:ss";
        String dateToFormat = "2022-08-22 04:22:100";
        Date formattedDate = parseDate(dateToFormat, format);
        System.out.println(formattedDate);
    }

    static Date parseDate(String date, String format) throws ParseException {
        if (date.isEmpty()) return null;
        return new SimpleDateFormat(format).parse(date);
    }
}

Here, we are using the SimpleDateFormat java class for parsing the date string to Date. We can provide any valid format as needed instead of yyyy-MM-dd HH:mm:ss For e.g if we want to format "2022-08-22" to Date then we need to use the format "yyyy-MM-dd"

Parse Date to Date in String:

Now let's look into another example where we want to parse the date into the date string. Here we are trying to parse the current date to the desired format like "yyyy-MM-dd HH:mm:ss"

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateTimeUtils {

    public static void main(String[] args) throws ParseException {
        String format = "yyyy-MM-dd HH:mm:ss";
        Date dateToFormat = new Date();
        String formattedDate = formatDate(dateToFormat, format);
        System.out.println(formattedDate);
    }

    static String formatDate(Date date, String format) {
        return new SimpleDateFormat(format).format(date);
    }
}

We are using SimpleDateFormat class to format the desired Date to date in string

The format will be the desired valid format that might be "yyyy-MM-dd" as well.

Share:

Tuesday, July 26, 2022

Setup Http client for API using OkHttp in Java

In this tutorial, we will learn how to use OkHttp for HTTP requests while consuming APIs.

OkHttp is an HTTP client which will do HTTP efficiently by loading faster and saving bandwidth. Using OkHttp is easy. Its request/response API is designed with fluent builders and immutability. It supports both synchronous blocking calls and async calls with callbacks.

Setup the OkHttp dependency:

If we are using the external Jar file, download the jar from okhttp maven.

Follow this tutorial for setting up the Jar file How to add external jar or library on IntelliJ IDEA project

Setup in maven project:

Add in pom.xml

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.10.0</version>
</dependency>

Setup in Gradle project:

Add in build.gradle under "dependencies"

dependencies {
//other dependencies
 
compile("com.squareup.okhttp3:okhttp:4.10.0")
}

Implementation:

Let's create a sample java class HttpClient.java and set up the OkHttp3 client.

import okhttp3.MediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.Response

class HttpClient {

    public static final MediaType JSON = MediaType.get("application/json; charset=utf-8")


    public static Response doGetRequest(url, String apiKey = '') {
        Request.Builder requestBuilder = new Request.Builder()
                .url(url)
        Request request = setHeadersConfig(requestBuilder, apiKey).build();
        Response response = getClient().newCall(request).execute()
        return response.body().string();
    }

    public static String doPostRequest(String json, String url, String apiKey = '') {
        RequestBody body = RequestBody.create(json, JSON)
        Request.Builder requestBuilder = new Request.Builder()
                .url(url)
                .post(body)
        Request request = setHeadersConfig(requestBuilder, apiKey).build();
        Response response = getClient().newCall(request).execute()
        return response.body().string()
    }

    public static String doDeleteRequest(String json, String url, String apiKey = '') {
        RequestBody body = RequestBody.create(json, JSON)
        Request.Builder requestBuilder = new Request.Builder()
                .url(url)
                .delete(body)
        Request request = setHeadersConfig(requestBuilder, apiKey).build();
        Response response = getClient().newCall(request).execute()
        return response.body().string()
    }

    private static Request.Builder setHeadersConfig(Request.Builder requestBuilder, String apiKey) {
        requestBuilder.header("Content-Type", "application/json")
                .header("X-MBX-APIKEY", apiKey)
        return requestBuilder
    }

    private static OkHttpClient getClient() {
        return new OkHttpClient()
    }
}

This class contains the setup for the post, get and delete request.

Method setHeadersConfig is for setting up the headers we used sample X-MBX-APIKEY header configuration to set the API key. You can use your desired header configuration there.

We can pass the post body by formatting the data in JSON string.

Please don't forget to handle the Exception.

Getting Status Code:

We can get the status code from the Response response object in the above example as below:

 Response response = getClient().newCall(request).execute()
        int statusCode = response.code()

Using it from Java classes:

All the methods are static so we can simply use it from other java classes as below:

HttpClient.doPostRequest(json, url, apiKey)
Share:

Tuesday, July 19, 2022

Java round double to decimal places with up and down rounding

This is a quick tutorial on how we can round the double or float value to x decimal places

Here we are using DecimalFormat class to do so. Let's look at the example. Here we are creating DecimalFormatter.java class

Round to x decimal places:

import java.text.DecimalFormat;

public class DecimalFormatter {

    public static void main(String[] args) {
        // normal format
        System.out.println("Normal format: " + format(2.0451, "0.00"));
        System.out.println("Normal format add extra zero: " + format(2.045, "0.00000"));
    }
    
    private static String format(double value, String precision) {
        DecimalFormat df = new DecimalFormat(precision);
        return df.format(value);
    }
}    

In the above example, we are using a default rounding mode i.e DecimalFormat by default uses half even rounding mode to format. i.e if the decimal value is greater or equal to 5 then it will round up for that precision else use the same value for that precision. Here, 2.0451 will be formatted to 2.05 as we are using the two decimal place 0.00

Also, in the second example, we have a double value of 2.045 but we are trying to round 0.00000 five decimal places so it will add the extra 2 zeros to the double value.

Output:

Normal format: 2.05
Normal format add extra zero: 2.04500

Round Up to x decimal places:

import java.math.RoundingMode;
import java.text.DecimalFormat;

public class DecimalFormatter {

    public static void main(String[] args) {
        // round up format
        System.out.println("Round up format: " + formatRoundUp(1.03510001, "0.0000"));
    }
    
     private static String formatRoundUp(double value, String precision) {
        DecimalFormat df = new DecimalFormat(precision);
        df.setRoundingMode(RoundingMode.UP);
        return df.format(value);
    }
}    

Here, we are setting rounding mode to Up for DecimalFormat so it will round up the decimal value, whether it is greater or equal to 5 or not for the given decimal places.

Output:

Round up format: 1.0352

Round Down to x decimal places:

import java.math.RoundingMode;
import java.text.DecimalFormat;

public class DecimalFormatter {

    public static void main(String[] args) {
        // round down format
        System.out.println("Round down format: " + formatRoundDown(1.0316, "0.000"));
    }
    
	private static String formatRoundDown(double value, String precision) {
        DecimalFormat df = new DecimalFormat(precision);
        df.setRoundingMode(RoundingMode.DOWN);
        return df.format(value);
    }
}    

In the above example, we are setting rounding mode to Down such that it will round down the decimal value whether it is greater or equal to 5.

Output:

Round down format: 1.031
Share:

Saturday, July 9, 2022

Setup and run Appium for Android with Java client on Linux

How to set up and run Appium for android apps with Java client with Emulator setup.
1. Introduction:

Appium is an open-source automation tool for mobile applications. It is a cross-platform tool that is used to automate native, hybrid, and mobile web applications running on ios, android, and windows platforms. In this tutorial, we are going to set up Appium and run a sample example.


2. prerequisites:

  1. Installed JDK
  2. NodeJs
3. Download and run Appium:

Download the latest version of the Appium desktop App Image from here. Under "Assets" download AppImage for Linux. Go to download directory and open terminal. Give permission for the app image file.

 
chmod a+x

Now, execute the AppImage file either by double-clicking it or running the following command.
./AppImage
You can see as the Appium Ui started similar to this.


Now, start the Appium server with default port 4723 by clicking the "Start Server" button.




4. Setup Android Emulator:

In order to set up an Android Emulator, you can set it up via a command-line tool without installing an android studio but here, we are going to download an android studio and set up android SDK to run Emulator. For this download android studio from here. Extract the downloaded tar file.
 
tar -xvf android-studio.tar.gz

Go to the extracted directory cd to the bin directory you can see the studio.sh simply execute this sh file by opening the terminal. Which will popup the android studio setting import option. Select "Do not import settings". Click "Next", select "Standard" and verify the setting. Make sure to verify the path of SDK installed.


Click "Next" and "Finish" which will download the SDK on the SDK folder mentioned.

Now, we need to configure the android virtual device for this click "Configure" and select "AVD Manager".


If you see "dev/kvm device: permission denied" you can give permission for the user by using the command:
sudo chown yourLinuxUser /dev/kvm
Create a Virtual Device







After that, select the downloaded image and click "Next" and verify android virtual device configuration and click "Finish" to finish the virtual device setup. Now you can launch the virtual device in the Emulator.



Now, let's configure the SDK path in our system. For this open bashrc file by using the command:
 
  sudo gedit ~/.bashrc

Add the following line at the end of the file.
 
  export ANDROID_HOME=/home/kchapagain/Downloads/Sdk
export PATH=$ANDROID_HOME/emulator:$ANDROID_HOME/tools:$PATH
export PATH=$PATH:$ANDROID_HOME/platform-tools
Make sure you provided the correct download SDK path in "ANDROID_HOME" in my case it's "/home/kchapagain/Downloads/Sdk". Save the file and reload the changes by using the command:
 
  source ~/.bashrc

You can launch the device on Emulator via the command line so that you don't need to open the android studio each time you run the device. For this, open the terminal use command:
emulator -list-avds
Output:
Pixel_2_API_28
Pixel_2_API_28_2

Here, we have two virtual devices installed in your case it will output the list of devices downloaded.

Now, run the device on the Emulator using command:
emulator -avd Pixel_2_API_28
User your device name instead "Pixel_2_API_28". It will run the android virtual device on Emulator.

 
5. Download and load library for Java Client:

Download the Appium java client jar from here.



Similarly, download JUnit jar from here.  Download selenium from here.

Create a java project and create a package called "libs" under "src". Copy all the downloaded jar file in this package and load the jar file from Ide.

For IntelliJ Idea:

Go to: File >> Project Structure(Ctr + Alt + Shift + s) >> Libraries and click the + icon on the top left corner and select package.


6. Setup driver config for Appium:

Create a sample class to config the android driver. Here, we are using the sample calculator app to test and the driver setup config looks like below:
 @Before
    public static void setUp() throws MalformedURLException {
        DesiredCapabilities cap = new DesiredCapabilities();
        cap.setCapability("platformName", "Android");
        cap.setCapability("deviceName", "50b6ab0");
        cap.setCapability("appPackage", "");
        cap.setCapability("appActivity", "");
        cap.setCapability("automationName", "UiAutomator1");
        cap.setCapability("autoGrantPermissions", true);
        cap.setCapability("autoAcceptAlerts", "true");
        driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), cap);
    }
Share:

Tomcat Invalid character found in the request target

While running an application in the tomcat server, sometimes we might get the following error.

org.apache.coyote.http11.Http11Processor.service Error parsing HTTP request header
 Note: further occurrences of HTTP request parsing errors will be logged at DEBUG level.
	java.lang.IllegalArgumentException: Invalid character found in the request target [/index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=md5&vars[1][]=HelloThinkPHP21 ]. The valid characters are defined in RFC 7230 and RFC 3986
		at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:509)
		at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:511)
		at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
		at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:831)
		at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1673)
		at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
		at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
		at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
		at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
		at java.lang.Thread.run(Thread.java:748)

First, make sure that the domain pointed to the server is working fine or not expired.

From tomcat, it is suggested that

The HTTP/1.1 specification requires that certain characters are %nn encoded when used in URI query strings. Unfortunately, many user agents including all the major browsers are not compliant with this specification and use these characters in unencoded form. To prevent Tomcat rejecting such requests, this attribute may be used to specify the additional characters to allow. If not specified, no additional characters will be allowed. The value may be any combination of the following characters: " < > [ \ ] ^ ` { | } . Any other characters present in the value will be ignored.

Tomcat increased its security and no longer allows raw square brackets or the above characters in the query string.

In order to resolve this issue use relaxedQueryChars attributes inside the server.xml file of tomcat server. Make sure to back up the server.xml file before we change it.

Open the server.xml file using vim or vi and type Shift + i

sudo vim /path_to_tomcat/conf/server.xml

Press Esc and hit :wq! and save the changes

Restart the tomcat server.

Share:

Tuesday, January 25, 2022

Install OpenCv in Linux/Ubuntu for Java Application.


1. Introduction:

In this blog post, we are going to install and set up OpenCV in ubuntu os for the java applications. OpenCV is a widely used great computer vision library. You can learn more about the OpenCV tutorial from here. For documentation, you can follow here. We will also cover some tutorials for Java bindings too.
 



2. Download OpenCV:

You can download OpenCV from the public Github repository of OpenCV or from their official website from here.  Select the desired version and click "Sources", which will download the zip file. Unzip the file.
unzip opencv-4.3.0.zip

 
3. Build OpenCV:

In order to build OpenCV, go to the OpenCV path in my case it's under "/opt/opencv-4.3.0" so I will use the same.

Create a directory to build.
mkdir build
  cd build
Now, if you don't have installed cmake software please install it using the below command
sudo apt-get  install cmake
Next, is to generate and configure with cmake for building executables in our system.
cmake -DBUILD_SHARED_LIBS=OFF ..
Note: When OpenCV is built as a set of static libraries (-DBUILD_SHARED_LIBS=OFF option) the Java bindings dynamic library is all-sufficient, i.e. doesn’t depend on other OpenCV libs, but includes all the OpenCV code inside. 

Make sure the output of the above command looks like below:


If it doesn't find the ant and java then you may get the following output: 
Java:
   ant:                         NO
   JNI:                         NO
   Java tests:                 YES
For this, install and setup your java and install ant
sudo apt install openjdk-8-jdk
sudo apt-get install ant

If you are still getting ant as NO then try the following command to install ant
sudo snap install ant --classic
Now start the build
make -j4
Note: Be careful not to run out of memory during the build. We need 4GB of memory per core. For example, if we compile with 4 cores (e.g. make -j4) we need a machine with at least 16GB of RAM.

The output look likes this and it will take some time.


If everything is fine, you successfully build OpenCV. Make sure the following files are packaged in the corresponding directory.
/opt/opencv-4.3.0/build/lib/libopencv_java430.so
/opt/opencv-4.3.0/build/bin/opencv-430.jar
The path of those files is created according to your OpenCV version and directory. You need to make sure the so and jar file must be created. This jar file contains the java wrapper code which we will used in the sample example.

 



4. Run Sample Example:

Now we are going to add the compiled jar file in our project library.

For IntelliJ Idea:

Go to : File >> Project Structure >> Libraries (under project settings)

you can see the + icon at the top left, to add a new project library click on it, and select java and add the path of previously created jar file i.e opencv-430.jar. 

It's time to run a sample test example.
import org.opencv.core.CvType;
import org.opencv.core.Mat;

public class SampleTest {

    public static void main(String[] args) {
        System.load("/opt/opencv-4.3.0/build/lib/libopencv_java430.so");
        Mat mat = Mat.eye(3, 3, CvType.CV_8UC1);
        System.out.println("mat = " + mat.dump());
    }
}
Make sure that you loaded your corresponding .so file.

Output:
mat = [  1,   0,   0;
   0,   1,   0;
   0,   0,   1]
For those who are running OpenCV in an existing project, you can set up with Gradle project as below:

For Gradle:

Copy the jar file in your project directory package for e.g "libs" and add following inside dependencies in build.gradle file.
dependencies {
//other dependencies

compile fileTree(dir: 'libs', include: '*.jar')
}
Share:

Friday, January 21, 2022

Read Dicom file meatadata using dcm4che in Java

How to read Dicom file metadata using dcm4che in Java.

dcm4che is a collection of open-source applications and utilities for the healthcare enterprise application for processing, manipulating, and analysis of medical images.

Please follow the tutorial for setting the dcm4che in our java application.

Let's create a java class DicomMetadataReader.java to read the available metadata in the given Dicom file.

public static void main(String[] args) {
        String inputDicomFilePath = "path/to/dicom/N2D_0001.dcm";
        try {
            readMetadata(inputDicomFilePath);
        } catch (IOException e) {
            System.out.println("Error due to: "+e.getMessage());
        }
    }
private static void readMetadata(String dicomFilePath) throws IOException {
        File file = new File(dicomFilePath);
        DicomInputStream dis = new DicomInputStream(file);
        Attributes attributes = dis.readDataset();
        int[] tags = attributes.tags();
        System.out.println("Total tag found in dicom file: "+tags.length);
        for (int tag: tags) {
            String tagAddress = TagUtils.toString(tag);
            String tagValue = attributes.getString(tag);
            System.out.println("Tag Address: " + tagAddress + " Value: " + tagValue);
        }
        dis.close();
    }

Here, we are using standard classes from the dcm4che core library jar file. DicomInputStream will read the file. We are reading the available dataset of the file and getting tags. After that, we are looping through the available tags and get the tag address and corresponding tag value.

The sample Output:

Total tag found in dicom file: 54
Tag Address: (0008,0008) Value: DERIVED
Tag Address: (0008,0016) Value: 1.2.840.10008.5.1.4.1.1.4
Tag Address: (0008,0018) Value: 1.2.826.0.1.3680043.2.1143.1590429688519720198888333603882344634
Tag Address: (0008,0020) Value: 20130717
Tag Address: (0008,0021) Value: 20130717
Tag Address: (0008,0022) Value: 20130717
Tag Address: (0008,0023) Value: 20130717
Tag Address: (0008,0030) Value: 141500
Tag Address: (0008,0031) Value: 142035.93000
Tag Address: (0008,0032) Value: 132518
Tag Address: (0008,0033) Value: 142035.93
Tag Address: (0008,0050) Value: null
Tag Address: (0008,0060) Value: MR
Tag Address: (0008,0070) Value: BIOLAB
Tag Address: (0008,0080) Value: null
Tag Address: (0008,0090) Value: null
Tag Address: (0008,1030) Value: Hanke_Stadler^0024_transrep
Tag Address: (0008,103E) Value: anat-T1w
Tag Address: (0008,1090) Value: nifti2dicom
Tag Address: (0010,0010) Value: Jane_Doe
Tag Address: (0010,0020) Value: 02
Tag Address: (0010,0030) Value: 19660101
Tag Address: (0010,0040) Value: F
Tag Address: (0010,1000) Value: null
Tag Address: (0010,1010) Value: 42
Tag Address: (0010,1030) Value: 75
Tag Address: (0010,21C0) Value: 4
Tag Address: (0018,0050) Value: 0.666666686534882
Tag Address: (0018,0088) Value: 0.666666686534882
Tag Address: (0018,1020) Value: 0.4.11
Tag Address: (0018,1030) Value: anat-T1w
Tag Address: (0020,000D) Value: 1.2.826.0.1.3680043.2.1143.2592092611698916978113112155415165916
Tag Address: (0020,000E) Value: 1.2.826.0.1.3680043.2.1143.515404396022363061013111326823367652
Tag Address: (0020,0010) Value: 433724515
Tag Address: (0020,0011) Value: 401
Tag Address: (0020,0012) Value: 1
Tag Address: (0020,0013) Value: 1
Tag Address: (0020,0020) Value: L
Tag Address: (0020,0032) Value: -91.4495864331908
Tag Address: (0020,0037) Value: 0.999032176441525
Tag Address: (0020,0052) Value: 1.2.826.0.1.3680043.2.1143.6856184167807409206647724161920598374
Tag Address: (0028,0002) Value: 1
Tag Address: (0028,0004) Value: MONOCHROME2
Tag Address: (0028,0010) Value: 384
Tag Address: (0028,0011) Value: 274
Tag Address: (0028,0030) Value: 0.666666686534882
Tag Address: (0028,0100) Value: 16
Tag Address: (0028,0101) Value: 16
Tag Address: (0028,0102) Value: 15
Tag Address: (0028,0103) Value: 1
Tag Address: (0028,1052) Value: 0
Tag Address: (0028,1053) Value: 1
Tag Address: (0028,1054) Value: US
Tag Address: (7FE0,0010) Value: 0

Please follow the Dicom standard library for tag and its description.

The overall code implementation looks as below:

package dicom.dcm4che;

import org.dcm4che3.data.Attributes;
import org.dcm4che3.io.DicomInputStream;
import org.dcm4che3.util.TagUtils;

import java.io.File;
import java.io.IOException;

public class DicomMetadataReader {

    public static void main(String[] args) {
        String inputDicomFilePath = "path/to/dicom/N2D_0001.dcm";
        try {
            readMetadata(inputDicomFilePath);
        } catch (IOException e) {
            System.out.println("Error due to: "+e.getMessage());
        }
    }

    private static void readMetadata(String dicomFilePath) throws IOException {
        File file = new File(dicomFilePath);
        DicomInputStream dis = new DicomInputStream(file);
        Attributes attributes = dis.readDataset();
        int[] tags = attributes.tags();
        System.out.println("Total tag found in dicom file: "+tags.length);
        for (int tag: tags) {
            String tagAddress = TagUtils.toString(tag);
            String tagValue = attributes.getString(tag);
            System.out.println("Tag Address: " + tagAddress + " Value: " + tagValue);
        }
        dis.close();
    }
}
Share:

Read the Dicom header information using dcm4che in Java

In this tutorial, we are going to learn how to get the Dicom file header information using dcm4che.

DICOM (Digital Imaging and Communications in Medicine) is the ubiquitous standard in the radiology and cardiology imaging industry for the exchange and management of images and image-related information.

For Dicom standard information please visit Dicom Library.

Before start writing code we need to use a couple of jar files. Download all the Dicom library files from the bundle. This bundle contains a lot of jar files we will use only the desired one.

Extract the downloaded file, go to the lib folder and grab two jar files. dcm4che-core-5.25.1.jar and another is slf4j-api-1.7.32.jar, the verison might be different at the time of implementation.

Now, create a folder called libs inside the project directory and add the jar file and load it from the IDE.

Loading Jar file in Maven Project:

<dependency>
          <groupId>com.dcm4che</groupId>
          <artifactId>dcm4che</artifactId>
          <version>20220117</version>
          <scope>system</scope>
          <systemPath>${basedir}/libs/dcm4che-core-5.25.1.jar</systemPath>
      </dependency>
<dependency>
          <groupId>com.slf4j</groupId>
          <artifactId>slf4j</artifactId>
          <version>20220217</version>
          <scope>system</scope>
          <systemPath>${basedir}/libs/slf4j-api-1.7.32.jar</systemPath>
      </dependency>

Note: use the downloaded jar file name.

Loading Jar in Gradle Project:

Add the following inside dependencies in build.gradle file.

dependencies {
//other dependencies
 
compile fileTree(dir: 'libs', include: '*.jar')
}

Let's create the java class ReadHeaderInfo.java to read the header information

public static void main(String[] args) {
        String inputDicomFilePath = "path/to/dicom/N2D_0001.dcm";
        try {
            readHeader(inputDicomFilePath);
        } catch (IOException e) {
            System.out.println("Error due to: "+e.getMessage());
        }
    }
private static void readHeader(String inputPath) throws IOException {
        File file = new File(inputPath);
        DicomInputStream dis = new DicomInputStream(file);
        Attributes attributes = dis.readDataset();
        int[] tags = attributes.tags();
        System.out.println("Total tag found in the given dicom file: "+tags.length);
        for (int tag: tags) {
            String tagAddress = TagUtils.toString(tag);
            String vr = attributes.getVR(tag).toString();
            System.out.println("Tag Address: " + tagAddress + " VR: " + vr);
        }
    }

Basically, we are using the Dicom-core classes from the jar file to read the file information. We are providing the sample Dicom file and reading the Tag Address and VR available in that file. 

DicomInputStream is used to read the file. dis.readDataset() read all the tags available in that file and finally, we are getting Tag Address and VR and outputting the values.

Output:

Total tag found in the given dicom file: 54
Tag Address: (0008,0008) VR: CS
Tag Address: (0008,0016) VR: UI
Tag Address: (0008,0018) VR: UI
Tag Address: (0008,0020) VR: DA
Tag Address: (0008,0021) VR: DA
Tag Address: (0008,0022) VR: DA
Tag Address: (0008,0023) VR: DA
Tag Address: (0008,0030) VR: TM
Tag Address: (0008,0031) VR: TM
Tag Address: (0008,0032) VR: TM
Tag Address: (0008,0033) VR: TM
Tag Address: (0008,0050) VR: SH
Tag Address: (0008,0060) VR: CS
Tag Address: (0008,0070) VR: LO
Tag Address: (0008,0080) VR: LO
Tag Address: (0008,0090) VR: PN
Tag Address: (0008,1030) VR: LO
Tag Address: (0008,103E) VR: LO
Tag Address: (0008,1090) VR: LO
Tag Address: (0010,0010) VR: PN
Tag Address: (0010,0020) VR: LO
Tag Address: (0010,0030) VR: DA
Tag Address: (0010,0040) VR: CS
Tag Address: (0010,1000) VR: LO
Tag Address: (0010,1010) VR: AS
Tag Address: (0010,1030) VR: DS
Tag Address: (0010,21C0) VR: US
Tag Address: (0018,0050) VR: DS
Tag Address: (0018,0088) VR: DS
Tag Address: (0018,1020) VR: LO
Tag Address: (0018,1030) VR: LO
Tag Address: (0020,000D) VR: UI
Tag Address: (0020,000E) VR: UI
Tag Address: (0020,0010) VR: SH
Tag Address: (0020,0011) VR: IS
Tag Address: (0020,0012) VR: IS
Tag Address: (0020,0013) VR: IS
Tag Address: (0020,0020) VR: CS
Tag Address: (0020,0032) VR: DS
Tag Address: (0020,0037) VR: DS
Tag Address: (0020,0052) VR: UI
Tag Address: (0028,0002) VR: US
Tag Address: (0028,0004) VR: CS
Tag Address: (0028,0010) VR: US
Tag Address: (0028,0011) VR: US
Tag Address: (0028,0030) VR: DS
Tag Address: (0028,0100) VR: US
Tag Address: (0028,0101) VR: US
Tag Address: (0028,0102) VR: US
Tag Address: (0028,0103) VR: US
Tag Address: (0028,1052) VR: DS
Tag Address: (0028,1053) VR: DS
Tag Address: (0028,1054) VR: LO
Tag Address: (7FE0,0010) VR: OW

Extracting the header information is important to visualize the Dicom file information.

The overall code implementation looks as below:

package dicom.dcm4che;

import org.dcm4che3.data.Attributes;
import org.dcm4che3.io.DicomInputStream;
import org.dcm4che3.util.TagUtils;

import java.io.File;
import java.io.IOException;

public class ReadHeaderInfo {

    public static void main(String[] args) {
        String inputDicomFilePath = "path/to/dicom/N2D_0001.dcm";
        try {
            readHeader(inputDicomFilePath);
        } catch (IOException e) {
            System.out.println("Error due to: "+e.getMessage());
        }
    }

    private static void readHeader(String inputPath) throws IOException {
        File file = new File(inputPath);
        DicomInputStream dis = new DicomInputStream(file);
        Attributes attributes = dis.readDataset();
        int[] tags = attributes.tags();
        System.out.println("Total tag found in the given dicom file: "+tags.length);
        for (int tag: tags) {
            String tagAddress = TagUtils.toString(tag);
            String vr = attributes.getVR(tag).toString();
            System.out.println("Tag Address: " + tagAddress + " VR: " + vr);
        }
        dis.close();
    }

}
Share:

Read the files inside directory(folder) and subdirectory(sub folder) using Java 8+

In this tutorial, we are going to learn how we can read all the files and specific files inside the directory and subdirectory.

Let's create a sample class FileReader.java

Read all the directory subdirectory and files:

 public static void main(String[] args) {
        String directoryPath = "path_to_directory";
        try {
            List<String> allFiles = listFiles(directoryPath);
            for (String filePath : allFiles) {
                System.out.println(filePath);
            }
        } catch (IOException e) {
            System.out.println("Error due to: " + e.getMessage());
        }
    }
    public static List<String> listFiles(String directoryPath) throws IOException {
        Path start = Paths.get(directoryPath);
        try (Stream<Path> stream = Files.walk(start, Integer.MAX_VALUE)) {
            return stream
                    .map(String::valueOf)
                    .sorted()
                    .collect(Collectors.toList());
        }
    }

listFiles() method list all the directory, subdirectory, and files present inside the given directory. We are using Files.walk which will return stream API; list the files, directory and subdirectory by walking through the start directory to the max level of the subdirectory. Integer.MAX_VALUE will go through the maximum level of subdirectories available.

Read all the files and folders inside the current directory:

    public static List<String> listFiles(String directoryPath) throws IOException {
        Path start = Paths.get(directoryPath);
        try (Stream<Path> stream = Files.walk(start, 1)) {
            return stream
                    .map(String::valueOf)
                    .sorted()
                    .collect(Collectors.toList());
        }
    }

Here, we are using 1 as a maximum level value instead of Integer.MAX_VALUE, so it will read the current directory.

Only read the files inside the directory and subdirectory:

    public static List<String> listFiles(String directoryPath) throws IOException {
        Path start = Paths.get(directoryPath);
        try (Stream<Path> stream = Files.walk(start, Integer.MAX_VALUE)) {
            return stream
                    .map(String::valueOf)
                    .filter(file -> !new File(file).isDirectory())
                    .sorted()
                    .collect(Collectors.toList());
        }
    }

We are adding the filter that will only read files but not the directory or subdirectory and returns only files present inside the given directory.

Read Specific files inside directory and subdirectory

Suppose we want to only read .jpg or .png or .zip files present inside the directory and subdirectory. In this case, we will apply the filter to read only the desired extension file.

public static void main(String[] args) {
        String directoryPath = "path_to_directory";
        try {
            String fileExtensionToRead = ".png";
            List<String> pngFiles = listFiles(directoryPath, fileExtensionToRead);
            for (String filePath : pngFiles) {
                System.out.println(filePath);
            }
        } catch (IOException e) {
            System.out.println("Error due to: " + e.getMessage());
        }
    }
private static List<String> listFiles(String directoryPath, String ext) throws IOException {
        Path start = Paths.get(directoryPath);
        try (Stream<Path> stream = Files.walk(start, Integer.MAX_VALUE)) {
            return stream
                    .map(String::valueOf)
                    .filter(file -> file.contains(ext))
                    .sorted()
                    .collect(Collectors.toList());
        }
    }

We are using a filter such that it will list the file if the filename contains the given extension. We can customize this filter as per our requirements.

The overall code implementation looks as below:

package io;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FileReader {

    public static void main(String[] args) {
        String directoryPath = "path_to_directory";
        try {
            List<String> allFiles = listFiles(directoryPath);
            for (String filePath : allFiles) {
                System.out.println(filePath);
            }
            String fileExtensionToRead = ".png";
            List<String> pngFiles = listFiles(directoryPath, fileExtensionToRead);
            for (String filePath : pngFiles) {
                System.out.println(filePath);
            }
        } catch (IOException e) {
            System.out.println("Error due to: " + e.getMessage());
        }
    }

    public static List<String> listFiles(String directoryPath) throws IOException {
        Path start = Paths.get(directoryPath);
        try (Stream<Path> stream = Files.walk(start, Integer.MAX_VALUE)) {
            return stream
                    .map(String::valueOf)
                    .filter(file -> !new File(file).isDirectory())
                    .sorted()
                    .collect(Collectors.toList());
        }
    }

    private static List<String> listFiles(String directoryPath, String ext) throws IOException {
        Path start = Paths.get(directoryPath);
        try (Stream<Path> stream = Files.walk(start, Integer.MAX_VALUE)) {
            return stream
                    .map(String::valueOf)
                    .filter(file -> file.contains(ext))
                    .sorted()
                    .collect(Collectors.toList());
        }
    }
}
Share:

Thursday, January 20, 2022

How to unzip the zip file and zip the folder in Java

In this tutorial, we are going to learn how to zip the given folder and unzip the zip file using Java.

Zip the folder or directory:

Let's create a java class ZipUnzip.java and create the main method.

public static void main(String[] args) {
        String folderInputPath = "path/of/folder/to/zip";
        String outputPath = "output/path/compressed.zip";
        try {
            zip(folderInputPath, outputPath);
        } catch (IOException e) {
            System.out.println("Error due to: " + e.getMessage());
        }
    }

Now, create a method to zip the folder.

private static void zip(String folderInputPath, String outputPath) throws IOException {
        Path sourceFolderPath = Paths.get(folderInputPath);
        File zipPath = new File(outputPath);
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipPath));
        Files.walkFileTree(sourceFolderPath, new SimpleFileVisitor<Path>() {
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                zos.putNextEntry(new ZipEntry(sourceFolderPath.relativize(file).toString()));
                Files.copy(file, zos);
                zos.closeEntry();
                return FileVisitResult.CONTINUE;
            }
        });
        zos.close();
    }

We are using java standard core library classes to zip. Files.walkFileTree() is used to navigate recursively through the directory tree. Once we execute the code we can see the zip file is created inside the output path provided. We can verify the compression manually by extracting it.

Unzip the zip file:

Let's create a method unzip() for unzipping the compressed file

 public static void main(String[] args) {
        String zipFilePath = "zip/file/path/compressed.zip";
        String zipOutputPath = "output/path/to/unzip";
        try {
            unzip(zipFilePath, zipOutputPath);
        } catch (IOException e) {
            System.out.println("Error due to: " + e.getMessage());
        }
    }
private static void unzip(String zipFilePath, String outputDir) throws IOException {
        byte[] buffer = new byte[1024];
        ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath));
        ZipEntry zipEntry = zis.getNextEntry();
        while (zipEntry != null) {
            File newFile = new File(outputDir + File.separator, zipEntry.getName());
            if (zipEntry.isDirectory()) {
                if (!newFile.isDirectory() && !newFile.mkdirs()) {
                    throw new IOException("Failed to create directory " + newFile);
                }
            } else {
                File parent = newFile.getParentFile();
                if (!parent.isDirectory() && !parent.mkdirs()) {
                    throw new IOException("Failed to create directory " + parent);
                }
                FileOutputStream fos = new FileOutputStream(newFile);
                int len = 0;
                while ((len = zis.read(buffer)) > 0) {
                    fos.write(buffer, 0, len);
                }
                fos.close();
            }
            zipEntry = zis.getNextEntry();
        }
        zis.closeEntry();
        zis.close();
    }

Here, we are using ZipInputStream to read the file and FileOutputStream to write the file to the provided output directory.

The overall code implementation looks like below:

package io;

import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

public class ZipUnzip {

    public static void main(String[] args) {
        String folderInputPath = "path/of/folder/to/zip";
        String outputPath = "output/path/compressed.zip";

        String zipFilePath = "zip/file/path/compressed.zip";
        String zipOutputPath = "output/path/to/unzip";
        try {
            zip(folderInputPath, outputPath);
            unzip(zipFilePath, zipOutputPath);
        } catch (IOException e) {
            System.out.println("Error due to: " + e.getMessage());
        }
    }

    private static void zip(String folderInputPath, String outputPath) throws IOException {
        Path sourceFolderPath = Paths.get(folderInputPath);
        File zipPath = new File(outputPath);
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipPath));
        Files.walkFileTree(sourceFolderPath, new SimpleFileVisitor<Path>() {
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                zos.putNextEntry(new ZipEntry(sourceFolderPath.relativize(file).toString()));
                Files.copy(file, zos);
                zos.closeEntry();
                return FileVisitResult.CONTINUE;
            }
        });
        zos.close();
    }

    private static void unzip(String zipFilePath, String outputDir) throws IOException {
        byte[] buffer = new byte[1024];
        ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath));
        ZipEntry zipEntry = zis.getNextEntry();
        while (zipEntry != null) {
            File newFile = new File(outputDir + File.separator, zipEntry.getName());
            if (zipEntry.isDirectory()) {
                if (!newFile.isDirectory() && !newFile.mkdirs()) {
                    throw new IOException("Failed to create directory " + newFile);
                }
            } else {
                File parent = newFile.getParentFile();
                if (!parent.isDirectory() && !parent.mkdirs()) {
                    throw new IOException("Failed to create directory " + parent);
                }
                FileOutputStream fos = new FileOutputStream(newFile);
                int len = 0;
                while ((len = zis.read(buffer)) > 0) {
                    fos.write(buffer, 0, len);
                }
                fos.close();
            }
            zipEntry = zis.getNextEntry();
        }
        zis.closeEntry();
        zis.close();
    }
}
Share:

Monday, January 17, 2022

Read Dicom Image metadata using PixelMed in Java

In this tutorial, we are going to learn how to get Dicom image metadata using PixelMed toolkit using Java.

DICOM (Digital Imaging and Communications in Medicine) is the ubiquitous standard in the radiology and cardiology imaging industry for the exchange and management of images and image-related information.

DICOM is also used in other images related medical fields, such as pathology, endoscopy, dentistry, ophthalmology, and dermatology.

PixelMed Java DICOM Toolkit is a stand-alone DICOM toolkit that implements code for reading and creating DICOM data, DICOM network and file support, a database of DICOM objects, support for display of directories, images, reports, and spectra, and DICOM object validation.

The toolkit is an implementation, which does not depend on any other DICOM tools. This is the freely available pure Java tools for compression and XML and database support.

Download the .jar file from PixelMed Jar. Create a folder called libs inside the project directory and add the jar file and load it from the Ide.

Loading Jar file in Maven Project:

Add the following system dependency inside pom.xml file.

<dependency>
           <groupId>com.pixelmed</groupId>
           <artifactId>pixelmed</artifactId>
           <version>20220117</version>
           <scope>system</scope>
           <systemPath>${basedir}/libs/pixelmed.jar</systemPath>
       </dependency>

Note: use the downloaded jar file name.

Loading Jar in Gradle Project:

Add the following inside dependencies in build.gradle file.

dependencies {
//other dependencies
 
compile fileTree(dir: 'libs', include: '*.jar')
}

Now, let's create a sample java class called ReadMetaDataPixelMed.java and create the main method to execute the code.

    private static AttributeList attributeList = new AttributeList();
public static void main(String[] args) {
        String dcmFilePath = "/path_to_dicom_image/N2D_0001.dcm";
        try {
            readAttributes(dcmFilePath);
            Map<String, String> metaData = readMetadata();
            for (Map.Entry<String, String> entry :metaData.entrySet()) {
                System.out.println(entry.getKey()+" : "+entry.getValue());
            }
        }catch (Exception e) {
            System.out.println("Error due to: "+e.getMessage());
        }
    }

Create the methods used inside it.

private static void readAttributes(String dcmFilePath) throws DicomException, IOException {
        attributeList.read(new File(dcmFilePath));
    }

readAttributes method read the attributes or tag of the Dicom image. For Dicom library Tags and their description please visit Dicom Library.

 private static Map<String, String> readMetadata() throws DicomException {
        Map<String, String> metaData = new LinkedHashMap<>();
        metaData.put("Patient Name", getTagInformation(TagFromName.PatientName));
        metaData.put("Patient ID", getTagInformation(TagFromName.PatientID));
        metaData.put("Transfer Syntax", getTagInformation(TagFromName.TransferSyntaxUID));
        metaData.put("SOP Class", getTagInformation(TagFromName.SOPClassUID));
        metaData.put("Modality", getTagInformation(TagFromName.Modality));
        metaData.put("Samples Per Pixel", getTagInformation(TagFromName.SamplesPerPixel));
        metaData.put("Photometric Interpretation", getTagInformation(TagFromName.PhotometricInterpretation));
        metaData.put("Pixel Spacing", getTagInformation(TagFromName.PixelSpacing));
        metaData.put("Bits Allocated", getTagInformation(TagFromName.BitsAllocated));
        metaData.put("Bits Stored", getTagInformation(TagFromName.BitsStored));
        metaData.put("High Bit", getTagInformation(TagFromName.HighBit));
        SourceImage img = new com.pixelmed.display.SourceImage(attributeList);
        metaData.put("Number of frames", String.valueOf(img.getNumberOfFrames()));
        metaData.put("Width", String.valueOf(img.getWidth()));
        metaData.put("Height", String.valueOf(img.getHeight()));
        metaData.put("Is Grayscale", String.valueOf(img.isGrayscale()));
        metaData.put("Pixel Data present", String.valueOf(!getTagInformation(TagFromName.PixelData).isEmpty()));
        return metaData;
    }

Here, we are reading some sample metadata. For more metadata lists please visit TagFromName.java class and use the desired one.

private static String getTagInformation(AttributeTag tag) {
        return Attribute.getDelimitedStringValuesOrDefault(attributeList, tag, "NOT FOUND");
    }

If the attribute is found then this method returns the value of that attribute if not found then return NOT FOUND text.

Output:

Patient Name : Jane_Doe
Patient ID : 02
Transfer Syntax : 1.2.840.10008.1.2
SOP Class : 1.2.840.10008.5.1.4.1.1.4
Modality : MR
Samples Per Pixel : 1
Photometric Interpretation : MONOCHROME2
Pixel Spacing : 0.666666686534882\0.699987828731537
Bits Allocated : 16
Bits Stored : 16
High Bit : 15
Number of frames : 1
Width : 274
Height : 384
Is Grayscale : true
Pixel Data present : true

The overall code implementation looks like below:

package dicom;

import com.pixelmed.dicom.*;
import com.pixelmed.display.SourceImage;

import java.io.File;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;

public class ReadMetaDataPixelMed {

    private static AttributeList attributeList = new AttributeList();

    public static void main(String[] args) {
        String dcmFilePath = "/path_to_dicom_image/N2D_0001.dcm";
        try {
            readAttributes(dcmFilePath);
            Map<String, String> metaData = readMetadata();
            for (Map.Entry<String, String> entry :metaData.entrySet()) {
                System.out.println(entry.getKey()+" : "+entry.getValue());
            }
        }catch (Exception e) {
            System.out.println("Error due to: "+e.getMessage());
        }
    }

    private static void readAttributes(String dcmFilePath) throws DicomException, IOException {
        attributeList.read(new File(dcmFilePath));
    }

    private static Map<String, String> readMetadata() throws DicomException {
        Map<String, String> metaData = new LinkedHashMap<>();
        metaData.put("Patient Name", getTagInformation(TagFromName.PatientName));
        metaData.put("Patient ID", getTagInformation(TagFromName.PatientID));
        metaData.put("Transfer Syntax", getTagInformation(TagFromName.TransferSyntaxUID));
        metaData.put("SOP Class", getTagInformation(TagFromName.SOPClassUID));
        metaData.put("Modality", getTagInformation(TagFromName.Modality));
        metaData.put("Samples Per Pixel", getTagInformation(TagFromName.SamplesPerPixel));
        metaData.put("Photometric Interpretation", getTagInformation(TagFromName.PhotometricInterpretation));
        metaData.put("Pixel Spacing", getTagInformation(TagFromName.PixelSpacing));
        metaData.put("Bits Allocated", getTagInformation(TagFromName.BitsAllocated));
        metaData.put("Bits Stored", getTagInformation(TagFromName.BitsStored));
        metaData.put("High Bit", getTagInformation(TagFromName.HighBit));
        SourceImage img = new com.pixelmed.display.SourceImage(attributeList);
        metaData.put("Number of frames", String.valueOf(img.getNumberOfFrames()));
        metaData.put("Width", String.valueOf(img.getWidth()));
        metaData.put("Height", String.valueOf(img.getHeight()));
        metaData.put("Is Grayscale", String.valueOf(img.isGrayscale()));
        metaData.put("Pixel Data present", String.valueOf(!getTagInformation(TagFromName.PixelData).isEmpty()));
        return metaData;
    }

    private static String getTagInformation(AttributeTag tag) {
        return Attribute.getDelimitedStringValuesOrDefault(attributeList, tag, "NOT FOUND");
    }
}
Share:

Sunday, January 16, 2022

Update the Dicom image metadata using Java

In this tutorial, we are going to learn how we can update the Dicom image metadata for the Dicom file.

We are using the SimpleITK library for this. SimpleITK library is very handy while working on medical image manipulation, processing, and analysis. Please follow our previous tutorial before starting this tutorial.

Simple Itk Installation for Java Application.

Read the Dicom Image metadata using Java

Let's create a sample java class ModifyMetaData.java. Create a main method that runs the update metadata code.

public static void main(String[] args) {
        String dcmImagePath = "input_dicom_image_path/N2D_0001.dcm";
        String metaDataKeyToUpdate = "0010|0020"; // Patient Id
        String metaDataValueToUpdate = "02L3000";
        String outputDcmPath = "output_dicom_image_path/output.dcm";
        try {
            ImageFileReader imageFileReader = getDcmImageFileReader(dcmImagePath);
            Image image = imageFileReader.execute(); // Read image
            updateMetaData(image, metaDataKeyToUpdate, metaDataValueToUpdate);
            writeImage(image, outputDcmPath);
        } catch (Exception e) {
            System.out.println("Error due to: " + e.getMessage());
        }
    }

Here, we are defining the input Dicom image path, metadata key to update. The metadata tag must be in the format of 0010|0020. Metadata value to update and the output Dicom image path to save the update Dicom image.

First, we are reading the Dicom image then we are updating the image with the tag value for the tag. Here we are using the patient Id tag to update. Please visit the Dicom library for the universal Dicom tag and its description.

Let's implement the method used in the main method.

private static ImageFileReader getDcmImageFileReader(String imagePath) {
        ImageFileReader imageFileReader = new ImageFileReader();
        imageFileReader.setImageIO("GDCMImageIO");
        imageFileReader.setFileName(imagePath);
        return imageFileReader;
    }

This method reads the Dicom image using the SimpleItk library.

private static void updateMetaData(Image image, String metaDataKey, String metaDataValue) {
        image.setMetaData(metaDataKey, metaDataValue);
    }

Here, we are simply updating the patient id provided with the value

private static void writeImage(Image image, String outputImagePath) {
        SimpleITK.writeImage(image, outputImagePath);
    }

Here, we are writing the updated Dicom image to the output file path. If we verify the file then the Dicom image, the Patient Id value is changed.

The overall code implementation looks as below:

package simpleitk;

import org.itk.simple.Image;
import org.itk.simple.ImageFileReader;
import org.itk.simple.SimpleITK;

public class ModifyMetaData {

    public static void main(String[] args) {
        String dcmImagePath = "input_dicom_image_path/N2D_0001.dcm";
        String metaDataKeyToUpdate = "0010|0020"; // Patient Id
        String metaDataValueToUpdate = "02L3000";
        String outputDcmPath = "output_dicom_image_path/output.dcm";
        try {
            ImageFileReader imageFileReader = getDcmImageFileReader(dcmImagePath);
            Image image = imageFileReader.execute(); // Read image
            updateMetaData(image, metaDataKeyToUpdate, metaDataValueToUpdate);
            writeImage(image, outputDcmPath);
        } catch (Exception e) {
            System.out.println("Error due to: " + e.getMessage());
        }
    }

    private static ImageFileReader getDcmImageFileReader(String imagePath) {
        ImageFileReader imageFileReader = new ImageFileReader();
        imageFileReader.setImageIO("GDCMImageIO");
        imageFileReader.setFileName(imagePath);
        return imageFileReader;
    }

    private static void updateMetaData(Image image, String metaDataKey, String metaDataValue) {
        image.setMetaData(metaDataKey, metaDataValue);
    }

    private static void writeImage(Image image, String outputImagePath) {
        SimpleITK.writeImage(image, outputImagePath);
    }
}
Share:

How to convert byte arrays to other data type and vice versa in Java

In this tutorial, we are going to learn how we can convert byte arrays to other data types like int, float, double, and vice versa. This conversion is the best practice for data transfer between different channels, I/O operations. 

By doing so, we can serialize and deserialize without changing the underlying data. If we want to create the binary files of different data types then first convert them to byte array and store them into a file.

Let's create a sample java class called ByteConverter.java

Convert Integer to a Byte array:

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public static byte[] toBytes(int intValue) {
        int times = Integer.SIZE / Byte.SIZE;
        return getAllocateByteBuffer(times).putInt(intValue).array();
    }
public static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN;
private static ByteBuffer getAllocateByteBuffer(int var) {
        return ByteBuffer.allocate(var).order(LITTLE_ENDIAN);
    }

Here, we are creating the toBytes() method which gets the allocated buffer and converts the Integer value to the Byte array. We are using ByteOrder as little-endian. Little-endian stores the least-significant byte at the smallest address. For more details on types of Endian visit Endianness.

Create a main method to test.

 public static void main(String[] args) {
        int a = 289;
        byte[] bytes = toBytes(a);
        System.out.println(Arrays.toString(bytes));
    }

Output:

[33, 1, 0, 0]

Convert Byte array to Integer:

  public static int toInteger(byte[] bytes) {
        return getByteBuffer(bytes).getInt();
    }
private static ByteBuffer getByteBuffer(byte[] bytes) {
        return ByteBuffer.wrap(bytes).order(LITTLE_ENDIAN);
    }
public static void main(String[] args) {
        byte[] bytes = {33, 1, 0, 0};
        int b = toInteger(bytes);
        System.out.println(b);
    }

Output:

289

Convert Double to Bytes and Vice versa:

 public static byte[] toBytes(double doubleValue) {
        int times = Double.SIZE / Byte.SIZE;
        return getAllocateByteBuffer(times).putDouble(doubleValue).array();
    }
public static double toDouble(byte[] bytes) {
        return getByteBuffer(bytes).getDouble();
    }
public static void main(String[] args) {
        double d = 45.56;
        byte[] doubleToBytes = toBytes(d);
        System.out.println("Double to bytes: "+ Arrays.toString(doubleToBytes));
        double bytesToDouble = toDouble(doubleToBytes);
        System.out.println("Bytes to Double: " + bytesToDouble);
    }

Convert Float to Bytes and vice versa:

public static byte[] toBytes(float floatValue) {
        int times = Float.SIZE / Byte.SIZE;
        return getAllocateByteBuffer(times).putFloat(floatValue).array();
    }
public static float toFloat(byte[] bytes) {
        return getByteBuffer(bytes).getFloat();
    }
public static void main(String[] args) {
        float f = 15.0f;
        byte[] floatToBytes = toBytes(f);
        System.out.println("Float to bytes: "+ Arrays.toString(floatToBytes));
        double bytesToFloat = toFloat(floatToBytes);
        System.out.println("Bytes to Float: " + bytesToFloat);
    }

Convert Integer array to byte array and vice versa:

 public static byte[] byteArray(int[] intArray) {
        int times = Integer.SIZE / Byte.SIZE;
        byte[] bytes = new byte[intArray.length * times];
        for (int i = 0; i < intArray.length; i++) {
            getByteBuffer(bytes, i, times).putInt(intArray[i]);
        }
        return bytes;
    }
 public static int[] intArray(byte[] byteArray) {
        int times = Integer.SIZE / Byte.SIZE;
        int[] ints = new int[byteArray.length / times];
        for (int i = 0; i < ints.length; i++) {
            ints[i] = getByteBuffer(byteArray, i, times).getInt();
        }
        return ints;
    }
private static ByteBuffer getByteBuffer(byte[] bytes, int index, int times) {
        return ByteBuffer.wrap(bytes, index * times, times).order(LITTLE_ENDIAN);
    }
public static void main(String[] args) {
        int[] intArray = {3, 4, 6, 8};
        byte[] intByteArray = byteArray(intArray);
        System.out.println("Int array to byte array: "+ Arrays.toString(intByteArray));
        int[] byteArrayToIntArray = intArray(intByteArray);
        System.out.println("Byte array to Int array: "+ Arrays.toString(byteArrayToIntArray));
    }

Here, while converting from int array to byte array, we are looping through the int array and for each integer value, we are converting into bytes. and applying the reverse process while converting from byte array to integer array.

Convert Double array to Byte array and vice versa:

 public static byte[] byteArray(double[] doubleArray) {
        int times = Double.SIZE / Byte.SIZE;
        byte[] bytes = new byte[doubleArray.length * times];
        for (int i = 0; i < doubleArray.length; i++) {
            getByteBuffer(bytes, i, times).putDouble(doubleArray[i]);
        }
        return bytes;
    }
 public static double[] doubleArray(byte[] bytes) {
        int times = Double.SIZE / Byte.SIZE;
        double[] doubles = new double[bytes.length / times];
        for (int i = 0; i < doubles.length; i++) {
            doubles[i] = getByteBuffer(bytes, i, times).getDouble();
        }
        return doubles;
    }
public static void main(String[] args) {
        double[] doubleArray = {3.0, 4.0, 6.0, 8.0};
        byte[] doubleByteArray = byteArray(doubleArray);
        System.out.println("Double array to byte array: "+ Arrays.toString(doubleByteArray));
        double[] byteArrayToDoubleArray = doubleArray(doubleByteArray);
        System.out.println("Byte array to Double array: "+ Arrays.toString(byteArrayToDoubleArray));
    }

Convert Float array to Byte array and vice versa:

 public static byte[] byteArray(float[] floatArray) {
        int times = Float.SIZE / Byte.SIZE;
        byte[] bytes = new byte[floatArray.length * times];
        for (int i = 0; i < floatArray.length; i++) {
            getByteBuffer(bytes, i, times).putFloat(floatArray[i]);
        }
        return bytes;
    }
 public static float[] floatArray(byte[] bytes) {
        int times = Float.SIZE / Byte.SIZE;
        float[] floats = new float[bytes.length / times];
        for (int i = 0; i < floats.length; i++) {
            floats[i] = getByteBuffer(bytes, i, times).getFloat();
        }
        return floats;
    }
public static void main(String[] args) {
        float[] floatArray = {3.0f, 4.0f, 6.0f, 8.0f};
        byte[] floatByteArray = byteArray(floatArray);
        System.out.println("Float array to byte array: "+ Arrays.toString(floatByteArray));
        float[] byteArrayToFloatArray = floatArray(floatByteArray);
        System.out.println("Byte array to Float array: "+ Arrays.toString(byteArrayToFloatArray));

    }

The overall code implementation looks as below:

package io;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;

public class ByteConverter {

    public static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN;

    public static void main(String[] args) {
        int i = 289;
        byte[] intToBytes = toBytes(i);
        System.out.println("Int to bytes: "+ Arrays.toString(intToBytes));
        int byteToInt = toInteger(intToBytes);
        System.out.println("Bytes to Int: " + byteToInt);
        double d = 45.56;
        byte[] doubleToBytes = toBytes(d);
        System.out.println("Double to bytes: "+ Arrays.toString(doubleToBytes));
        double bytesToDouble = toDouble(doubleToBytes);
        System.out.println("Bytes to Double: " + bytesToDouble);
        float f = 15.0f;
        byte[] floatToBytes = toBytes(f);
        System.out.println("Float to bytes: "+ Arrays.toString(floatToBytes));
        double bytesToFloat = toFloat(floatToBytes);
        System.out.println("Bytes to Float: " + bytesToFloat);
        int[] intArray = {3, 4, 6, 8};
        byte[] intByteArray = byteArray(intArray);
        System.out.println("Int array to byte array: "+ Arrays.toString(intByteArray));
        int[] byteArrayToIntArray = intArray(intByteArray);
        System.out.println("Byte array to Int array: "+ Arrays.toString(byteArrayToIntArray));
        double[] doubleArray = {3.0, 4.0, 6.0, 8.0};
        byte[] doubleByteArray = byteArray(doubleArray);
        System.out.println("Double array to byte array: "+ Arrays.toString(doubleByteArray));
        double[] byteArrayToDoubleArray = doubleArray(doubleByteArray);
        System.out.println("Byte array to Double array: "+ Arrays.toString(byteArrayToDoubleArray));
        float[] floatArray = {3.0f, 4.0f, 6.0f, 8.0f};
        byte[] floatByteArray = byteArray(floatArray);
        System.out.println("Float array to byte array: "+ Arrays.toString(floatByteArray));
        float[] byteArrayToFloatArray = floatArray(floatByteArray);
        System.out.println("Byte array to Float array: "+ Arrays.toString(byteArrayToFloatArray));

    }

    public static byte[] toBytes(int intValue) {
        int times = Integer.SIZE / Byte.SIZE;
        return getAllocateByteBuffer(times).putInt(intValue).array();
    }

    public static byte[] toBytes(double doubleValue) {
        int times = Double.SIZE / Byte.SIZE;
        return getAllocateByteBuffer(times).putDouble(doubleValue).array();
    }

    public static byte[] toBytes(float floatValue) {
        int times = Float.SIZE / Byte.SIZE;
        return getAllocateByteBuffer(times).putFloat(floatValue).array();
    }

    public static byte[] byteArray(int[] intArray) {
        int times = Integer.SIZE / Byte.SIZE;
        byte[] bytes = new byte[intArray.length * times];
        for (int i = 0; i < intArray.length; i++) {
            getByteBuffer(bytes, i, times).putInt(intArray[i]);
        }
        return bytes;
    }

    public static byte[] byteArray(double[] doubleArray) {
        int times = Double.SIZE / Byte.SIZE;
        byte[] bytes = new byte[doubleArray.length * times];
        for (int i = 0; i < doubleArray.length; i++) {
            getByteBuffer(bytes, i, times).putDouble(doubleArray[i]);
        }
        return bytes;
    }

    public static byte[] byteArray(float[] floatArray) {
        int times = Float.SIZE / Byte.SIZE;
        byte[] bytes = new byte[floatArray.length * times];
        for (int i = 0; i < floatArray.length; i++) {
            getByteBuffer(bytes, i, times).putFloat(floatArray[i]);
        }
        return bytes;
    }

    public static int toInteger(byte[] bytes) {
        return getByteBuffer(bytes).getInt();
    }

    public static double toDouble(byte[] bytes) {
        return getByteBuffer(bytes).getDouble();
    }

    public static float toFloat(byte[] bytes) {
        return getByteBuffer(bytes).getFloat();
    }

    public static int[] intArray(byte[] byteArray) {
        int times = Integer.SIZE / Byte.SIZE;
        int[] ints = new int[byteArray.length / times];
        for (int i = 0; i < ints.length; i++) {
            ints[i] = getByteBuffer(byteArray, i, times).getInt();
        }
        return ints;
    }

    public static double[] doubleArray(byte[] bytes) {
        int times = Double.SIZE / Byte.SIZE;
        double[] doubles = new double[bytes.length / times];
        for (int i = 0; i < doubles.length; i++) {
            doubles[i] = getByteBuffer(bytes, i, times).getDouble();
        }
        return doubles;
    }

    public static float[] floatArray(byte[] bytes) {
        int times = Float.SIZE / Byte.SIZE;
        float[] floats = new float[bytes.length / times];
        for (int i = 0; i < floats.length; i++) {
            floats[i] = getByteBuffer(bytes, i, times).getFloat();
        }
        return floats;
    }

    private static ByteBuffer getByteBuffer(byte[] bytes) {
        return ByteBuffer.wrap(bytes).order(LITTLE_ENDIAN);
    }

    private static ByteBuffer getAllocateByteBuffer(int var) {
        return ByteBuffer.allocate(var).order(LITTLE_ENDIAN);
    }

    private static ByteBuffer getByteBuffer(byte[] bytes, int index, int times) {
        return ByteBuffer.wrap(bytes, index * times, times).order(LITTLE_ENDIAN);
    }
}

Share:

Saturday, January 8, 2022

How to unzip all the zip files present in folder and sub folders using java

In this tutorial, we are going to learn how we can unzip all the zip files present in the folder and sub-folders.

Steps to follow:

  • loop through the folder and subfolders and get the files inside it.
  • Check if the file is a zip file or not
  • If the file is a zip file then extract that file in the corresponding folder

Create a java class called UnzipFolderFile.java. First, let's implement the method which will loop through the folder and subfolders and read the zip file.

public static void searchZipFile(String directoryName) throws IOException {
        File directory = new File(directoryName);
        File[] fList = directory.listFiles();
        if (fList != null)
            for (File file : fList) {
                if (file.isDirectory()) {
                    searchZipFile(file.getAbsolutePath());
                } else if (file.isFile()) {
                    String ext = getFileExtension(file.getName());
                    if (ext.equals(".zip")) {
                        System.out.println("Zip file found");
                        String zipFilePath = file.getAbsolutePath();
                        String unZipOutputPath = getFileNameWithoutExtension(zipFilePath); // using apache commons i.o library
                        unzipFile(zipFilePath, unZipOutputPath);
                        searchZipFile(unZipOutputPath); // for nested zip file
                    }
                }
            }
    }

Here, we are using the recursion in order to search zip files inside sub-folders. If the current file is a directory then it will again search inside this directory. If it is a zip file then it will unzip that file. 

Note that we need to extract the nested zip file i.e zip file inside zip file case so, we used searchZipFile(unZipOutputPath); recursion.

In order to get the current folder, we are using the getFileNameWithoutExtension method which we gonna implement later. Now, let's implement the unzipFile method getFileExtension to get the extension of the file in order to test the current file is a zip file or not

 public static String getFileExtension(String fileName) {
        int lastIndexOf = fileName.lastIndexOf(".");
        if (lastIndexOf == -1) {
            return ""; // empty extension
        }
        return fileName.substring(lastIndexOf).toLowerCase();
    }
public static void unzipFile(String zipFilePath, String outputDir) throws IOException {
        byte[] buffer = new byte[1024];
        ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath));
        ZipEntry zipEntry = zis.getNextEntry();
        while (zipEntry != null) {
            File newFile = new File(outputDir + File.separator, zipEntry.getName());
            if (zipEntry.isDirectory()) {
                if (!newFile.isDirectory() && !newFile.mkdirs()) {
                    throw new IOException("Failed to create directory " + newFile);
                }
            } else {
                File parent = newFile.getParentFile();
                if (!parent.isDirectory() && !parent.mkdirs()) {
                    throw new IOException("Failed to create directory " + parent);
                }
                FileOutputStream fos = new FileOutputStream(newFile);
                int len = 0;
                while ((len = zis.read(buffer)) > 0) {
                    fos.write(buffer, 0, len);
                }
                fos.close();
            }
            zipEntry = zis.getNextEntry();
        }
        zis.closeEntry();
        zis.close();
    }

This method accepts the zip file path and output path to unzip and extract the zip file and output it in the output directory provided.

Now, create getFileNameWithoutExtension method to get the directory path. For this, we are using org.apache.commons.io library. For this, load the jar file in your IDE, or if you have existing projects like Maven or Gradle add the dependency as below.

For Gradle:

Add in build.gradle under "dependencies"

implementation 'org.apache.directory.studio:org.apache.commons.io:2.4'

For Maven:

Add in pom.xml

<dependency>
  <groupId>org.apache.directory.studio</groupId>
  <artifactId>org.apache.commons.io</artifactId>
  <version>2.4</version>
</dependency>
public static String getFileNameWithoutExtension(String name) {
        return FilenameUtils.removeExtension(name);
    }

Now, implement the main method to execute the program

public static void main(String[] args) {
        String directory = "path-to-folder";
        try {
            searchZipFile(directory);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

The overall implementation looks as below:

package io;

import org.apache.commons.io.FilenameUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class UnzipFolderFile {
    public static void main(String[] args) {
        String directory = "path-to-folder";
        try {
            searchZipFile(directory);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void searchZipFile(String directoryName) throws IOException {
        File directory = new File(directoryName);
        File[] fList = directory.listFiles();
        if (fList != null)
            for (File file : fList) {
                if (file.isDirectory()) {
                    searchZipFile(file.getAbsolutePath());
                } else if (file.isFile()) {
                    String ext = getFileExtension(file.getName());
                    if (ext.equals(".zip")) {
                        System.out.println("Zip file found");
                        String zipFilePath = file.getAbsolutePath();
                        String unZipOutputPath = getFileNameWithoutExtension(zipFilePath); // using apache commons i.o library
                        unzipFile(zipFilePath, unZipOutputPath);
                        searchZipFile(unZipOutputPath); // for nested zip file
                    }
                }
            }
    }

    public static void unzipFile(String zipFilePath, String outputDir) throws IOException {
        byte[] buffer = new byte[1024];
        ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath));
        ZipEntry zipEntry = zis.getNextEntry();
        while (zipEntry != null) {
            File newFile = new File(outputDir + File.separator, zipEntry.getName());
            if (zipEntry.isDirectory()) {
                if (!newFile.isDirectory() && !newFile.mkdirs()) {
                    throw new IOException("Failed to create directory " + newFile);
                }
            } else {
                File parent = newFile.getParentFile();
                if (!parent.isDirectory() && !parent.mkdirs()) {
                    throw new IOException("Failed to create directory " + parent);
                }
                FileOutputStream fos = new FileOutputStream(newFile);
                int len = 0;
                while ((len = zis.read(buffer)) > 0) {
                    fos.write(buffer, 0, len);
                }
                fos.close();
            }
            zipEntry = zis.getNextEntry();
        }
        zis.closeEntry();
        zis.close();
    }

    public static String getFileExtension(String fileName) {
        int lastIndexOf = fileName.lastIndexOf(".");
        if (lastIndexOf == -1) {
            return ""; // empty extension
        }
        return fileName.substring(lastIndexOf).toLowerCase();
    }

    public static String getFileNameWithoutExtension(String name) {
        return FilenameUtils.removeExtension(name);
    }
}
Share: