All Products
Search
Document Center

MaxCompute:Use a MaxCompute UDF to convert IPv4 or IPv6 addresses into geolocations

Last Updated:Mar 26, 2026

This tutorial walks you through building a Java user-defined function (UDF) in MaxCompute that maps IPv4 and IPv6 addresses to city and province-level geolocations. The UDF reads a local IP address library at runtime and is callable directly from SQL.

How it works

The UDF reads two file resources — ipv4.txt and ipv6.txt — that you upload to your MaxCompute project. When you call the UDF in a SQL SELECT statement, the evaluate() method detects whether the input is an IPv4 or IPv6 address, runs a binary search against the in-memory IP range table, and returns the matching city and province as a comma-separated string.

SQL SELECT → UDF evaluate() → binary search in IP library → "City, Province"

File resources in MaxCompute are made available to the UDF at runtime through ExecutionContext.readResourceFileAsStream(). The runtime copies these resources into the UDF's execution context, which is why the library files must be registered as file resources in your project — not just stored locally. For local debugging in MaxCompute Studio, place the files in the warehouse/example_project/_resources_ directory so the local runtime can find them.

Prerequisites

Before you begin, ensure that you have:

Usage notes

The IP address library provided in this tutorial is for reference only. The library file includes some Chinese characters, which does not affect test results. Maintain the library based on your own business requirements.

Step 1: Upload the IP address library

  1. Download the IP address library to your machine and decompress the archive. You get two files: ipv4.txt and ipv6.txt.

  2. Copy both files to the MaxCompute client installation directory: ...\odpscmd_public\bin.

  3. Start the MaxCompute client and connect to the target project.

  4. Upload the files as file resources:

    add file ipv4.txt -f;
    add file ipv6.txt -f;

    For more information, see Add resources.

  5. (Local debugging only) Copy ipv4.txt and ipv6.txt to the warehouse/example_project/_resources_ directory of your local MaxCompute Studio project.

Step 2: Connect to a MaxCompute project

  1. Connect MaxCompute Studio to your project. See Manage project connections.

  2. Create a MaxCompute Java module. See Create a MaxCompute Java module.

Step 3: Write the UDF

The UDF is built from three Java utility classes and one UDF class. All code in this step can be used without modification.

Create the Java utility classes

In IntelliJ IDEA, open the Project tab and go to src > main > java. Right-click java and choose New > Java Class to create each of the following classes.

Create a Java class

IpUtils

package com.aliyun.odps.udf.utils;

import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;

public class IpUtils {

    /**
     * Convert the data type of IP addresses from STRING to LONG.
     *
     * @param ipInString
     * IP addresses of the STRING type.
     * @return Return the IP addresses of the LONG type.
     */
    public static long StringToLong(String ipInString) {

        ipInString = ipInString.replace(" ", "");
        byte[] bytes;
        if (ipInString.contains(":"))
            bytes = ipv6ToBytes(ipInString);
        else
            bytes = ipv4ToBytes(ipInString);
        BigInteger bigInt = new BigInteger(bytes);
//        System.out.println(bigInt.toString());
        return bigInt.longValue();
    }


    /**
     * Convert the data type of IP addresses from STRING to LONG.
     *
     * @param ipInString
     * IP addresses of the STRING type.
     * @return Return the IP addresses of the STRING type that is converted from BIGINT.
     */
    public static String StringToBigIntString(String ipInString) {

        ipInString = ipInString.replace(" ", "");
        byte[] bytes;
        if (ipInString.contains(":"))
            bytes = ipv6ToBytes(ipInString);
        else
            bytes = ipv4ToBytes(ipInString);
        BigInteger bigInt = new BigInteger(bytes);
        return bigInt.toString();
    }

    /**
     * Convert the data type of IP addresses from BIGINT to STRING.
     *
     * @param ipInBigInt
     * IP addresses of the BIGINT type.
     * @return Return the IP addresses of the STRING type.
     */
    public static String BigIntToString(BigInteger ipInBigInt) {
        byte[] bytes = ipInBigInt.toByteArray();
        byte[] unsignedBytes = Arrays.copyOfRange(bytes, 1, bytes.length);
        // Remove the sign bit.
        try {
            String ip = InetAddress.getByAddress(unsignedBytes).toString();
            return ip.substring(ip.indexOf('/') + 1).trim();
        } catch (UnknownHostException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Convert the data type of IPv6 addresses into signed byte 17.
     */
    private static byte[] ipv6ToBytes(String ipv6) {
        byte[] ret = new byte[17];
        ret[0] = 0;
        int ib = 16;
        boolean comFlag=false;// IPv4/IPv6 flag.
        if (ipv6.startsWith(":"))// Remove the colon (:) from the start of IPv6 addresses.
            ipv6 = ipv6.substring(1);
        String groups[] = ipv6.split(":");
        for (int ig=groups.length - 1; ig > -1; ig--) {// Reverse scan.
            if (groups[ig].contains(".")) {
                // Both IPv4 and IPv6 addresses exist.
                byte[] temp = ipv4ToBytes(groups[ig]);
                ret[ib--] = temp[4];
                ret[ib--] = temp[3];
                ret[ib--] = temp[2];
                ret[ib--] = temp[1];
                comFlag = true;
            } else if ("".equals(groups[ig])) {
                // Zero-length compression. Calculate the number of missing groups.
                int zlg = 9 - (groups.length + (comFlag ? 1 : 0));
                while (zlg-- > 0) {// Set these groups to 0.
                    ret[ib--] = 0;
                    ret[ib--] = 0;
                }
            } else {
                int temp = Integer.parseInt(groups[ig], 16);
                ret[ib--] = (byte) temp;
                ret[ib--] = (byte) (temp >> 8);
            }
        }
        return ret;
    }

    /**
     * Convert the data type of IPv4 addresses into signed byte 5.
     */
    private static byte[] ipv4ToBytes(String ipv4) {
        byte[] ret = new byte[5];
        ret[0] = 0;
        // Find the positions of the periods (.) in the IP addresses of the STRING type.
        int position1 = ipv4.indexOf(".");
        int position2 = ipv4.indexOf(".", position1 + 1);
        int position3 = ipv4.indexOf(".", position2 + 1);
        // Convert the IP addresses of the STRING type between periods (.) into INTEGER.
        ret[1] = (byte) Integer.parseInt(ipv4.substring(0, position1));
        ret[2] = (byte) Integer.parseInt(ipv4.substring(position1 + 1,
                position2));
        ret[3] = (byte) Integer.parseInt(ipv4.substring(position2 + 1,
                position3));
        ret[4] = (byte) Integer.parseInt(ipv4.substring(position3 + 1));
        return ret;
    }


    /**
     * @param ipAdress IPv4 or IPv6 addresses of the STRING type.
     * @return 4:IPv4, 6:IPv6, 0: Invalid IP addresses.
     * @throws Exception
     */
    public static int isIpV4OrV6(String ipAdress) throws Exception {
        InetAddress address = InetAddress.getByName(ipAdress);
        if (address instanceof Inet4Address)
            return 4;
        else if (address instanceof Inet6Address)
            return 6;
        return 0;
    }


    /*
     * Check whether the IP address belongs to a specific IP section.
     *
     * ipSection The IP sections that are separated by hyphens (-).
     *
     * The IP address to check.
     */

    public static boolean ipExistsInRange(String ip, String ipSection) {

        ipSection = ipSection.trim();

        ip = ip.trim();

        int idx = ipSection.indexOf('-');

        String beginIP = ipSection.substring(0, idx);

        String endIP = ipSection.substring(idx + 1);

        return getIp2long(beginIP) <= getIp2long(ip)
                && getIp2long(ip) <= getIp2long(endIP);

    }

    public static long getIp2long(String ip) {

        ip = ip.trim();

        String[] ips = ip.split("\\.");

        long ip2long = 0L;

        for (int i = 0; i < 4; ++i) {

            ip2long = ip2long << 8 | Integer.parseInt(ips[i]);

        }
        return ip2long;

    }

    public static long getIp2long2(String ip) {

        ip = ip.trim();

        String[] ips = ip.split("\\.");

        long ip1 = Integer.parseInt(ips[0]);

        long ip2 = Integer.parseInt(ips[1]);

        long ip3 = Integer.parseInt(ips[2]);

        long ip4 = Integer.parseInt(ips[3]);

        long ip2long = 1L * ip1 * 256 * 256 * 256 + ip2 * 256 * 256 + ip3 * 256
                + ip4;

        return ip2long;

    }

    public static void main(String[] args) {
        System.out.println(StringToLong("2002:7af3:f3be:ffff:ffff:ffff:ffff:ffff"));
        System.out.println(StringToLong("54.38.72.63"));
    }

    private class Invalid{
        private Invalid()
        {

        }
    }
}

IpV4Obj

package com.aliyun.odps.udf.objects;

public class IpV4Obj {
    public long startIp ;
    public long endIp ;
    public String city;
    public String province;

    public IpV4Obj(long startIp, long endIp, String city, String province) {
        this.startIp = startIp;
        this.endIp = endIp;
        this.city = city;
        this.province = province;
    }

    @Override
    public String toString() {
        return "IpV4Obj{" +
                "startIp=" + startIp +
                ", endIp=" + endIp +
                ", city='" + city + '\'' +
                ", province='" + province + '\'' +
                '}';
    }

    public void setStartIp(long startIp) {
        this.startIp = startIp;
    }

    public void setEndIp(long endIp) {
        this.endIp = endIp;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public long getStartIp() {
        return startIp;
    }

    public long getEndIp() {
        return endIp;
    }

    public String getCity() {
        return city;
    }

    public String getProvince() {
        return province;
    }
}

IpV6Obj

package com.aliyun.odps.udf.objects;

public class IpV6Obj {
    public String startIp ;
    public String endIp ;
    public String city;
    public String province;

    public String getStartIp() {
        return startIp;
    }

    @Override
    public String toString() {
        return "IpV6Obj{" +
                "startIp='" + startIp + '\'' +
                ", endIp='" + endIp + '\'' +
                ", city='" + city + '\'' +
                ", province='" + province + '\'' +
                '}';
    }

    public IpV6Obj(String startIp, String endIp, String city, String province) {
        this.startIp = startIp;
        this.endIp = endIp;
        this.city = city;
        this.province = province;
    }

    public void setStartIp(String startIp) {
        this.startIp = startIp;
    }

    public String getEndIp() {
        return endIp;
    }

    public void setEndIp(String endIp) {
        this.endIp = endIp;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }
}

Create the UDF class

In the Project tab, go to src > main > java, right-click java, and choose New > MaxCompute Java.

Write a UDF

In the Create new MaxCompute java class dialog box, click UDF and enter IpLocation in the Name field. Then enter the following code:

package com.aliyun.odps.udf.udfFunction;

import com.aliyun.odps.udf.ExecutionContext;
import com.aliyun.odps.udf.UDF;
import com.aliyun.odps.udf.UDFException;
import com.aliyun.odps.udf.utils.IpUtils;
import com.aliyun.odps.udf.objects.IpV4Obj;
import com.aliyun.odps.udf.objects.IpV6Obj;
import java.io.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class IpLocation extends UDF {
    public static IpV4Obj[] ipV4ObjsArray;
    public static IpV6Obj[] ipV6ObjsArray;

    public IpLocation() {
        super();
    }

    @Override
    public void setup(ExecutionContext ctx) throws UDFException, IOException {
        //IPV4
        if(ipV4ObjsArray==null)
        {
            BufferedInputStream bufferedInputStream = ctx.readResourceFileAsStream("ipv4.txt");

            BufferedReader br = new BufferedReader(new InputStreamReader(bufferedInputStream));
            ArrayList<IpV4Obj> ipV4ObjArrayList=new ArrayList<>();
            String line = null;
            while ((line = br.readLine()) != null) {
                String[] f = line.split("\\|", -1);
                if(f.length>=5)
                {
                    long startIp = IpUtils.StringToLong(f[0]);
                    long endIp = IpUtils.StringToLong(f[1]);
                    String city=f[3];
                    String province=f[4];
                    IpV4Obj ipV4Obj = new IpV4Obj(startIp, endIp, city, province);
                    ipV4ObjArrayList.add(ipV4Obj);
                }
            }
            br.close();
            List<IpV4Obj> collect = ipV4ObjArrayList.stream().sorted(Comparator.comparing(IpV4Obj::getStartIp)).collect(Collectors.toList());
            ArrayList<IpV4Obj> basicIpV4DataList=(ArrayList)collect;
            IpV4Obj[] ipV4Objs = new IpV4Obj[basicIpV4DataList.size()];
            ipV4ObjsArray = basicIpV4DataList.toArray(ipV4Objs);
        }

        //IPV6
        if(ipV6ObjsArray==null)
        {
            BufferedInputStream bufferedInputStream = ctx.readResourceFileAsStream("ipv6.txt");
            BufferedReader br = new BufferedReader(new InputStreamReader(bufferedInputStream));
            ArrayList<IpV6Obj> ipV6ObjArrayList=new ArrayList<>();
            String line = null;
            while ((line = br.readLine()) != null) {
                String[] f = line.split("\\|", -1);
                if(f.length>=5)
                {
                    String startIp = IpUtils.StringToBigIntString(f[0]);
                    String endIp = IpUtils.StringToBigIntString(f[1]);
                    String city=f[3];
                    String province=f[4];
                    IpV6Obj ipV6Obj = new IpV6Obj(startIp, endIp, city, province);
                    ipV6ObjArrayList.add(ipV6Obj);
                }
            }
            br.close();
            List<IpV6Obj> collect = ipV6ObjArrayList.stream().sorted(Comparator.comparing(IpV6Obj::getStartIp)).collect(Collectors.toList());
            ArrayList<IpV6Obj> basicIpV6DataList=(ArrayList)collect;
            IpV6Obj[] ipV6Objs = new IpV6Obj[basicIpV6DataList.size()];
            ipV6ObjsArray = basicIpV6DataList.toArray(ipV6Objs);
        }

    }

    public String evaluate(String ip){
        if(ip==null||ip.trim().isEmpty()||!(ip.contains(".")||ip.contains(":")))
        {
            return null;
        }
        int ipV4OrV6=0;
        try {
            ipV4OrV6= IpUtils.isIpV4OrV6(ip);
        } catch (Exception e) {
            return null;
        }
        // IPv4 addresses are used.
        if(ipV4OrV6==4)
        {
            int i = binarySearch(ipV4ObjsArray, IpUtils.StringToLong(ip));
            if(i>=0)
            {
                IpV4Obj ipV4Obj = ipV4ObjsArray[i];
                return ipV4Obj.city+","+ipV4Obj.province;
            }else{
                return null;
            }
        } else if(ipV4OrV6==6)// IPv6 addresses are used.
        {
            int i = binarySearchIPV6(ipV6ObjsArray, IpUtils.StringToBigIntString(ip));
            if(i>=0)
            {
                IpV6Obj ipV6Obj = ipV6ObjsArray[i];
                return ipV6Obj.city+","+ipV6Obj.province;
            }else{
                return null;
            }
        } else{// IP addresses are invalid.
            return null;
        }

    }


    @Override
    public void close() throws UDFException, IOException {
        super.close();
    }

    private static int binarySearch(IpV4Obj[] array,long ip){
        int low=0;
        int hight=array.length-1;
        while (low<=hight)
        {
            int middle=(low+hight)/2;
            if((ip>=array[middle].startIp)&&(ip<=array[middle].endIp))
            {
                return middle;
            }
            if (ip < array[middle].startIp)
                hight = middle - 1;
            else {
                low = middle + 1;
            }
        }
        return -1;
    }


    private static int binarySearchIPV6(IpV6Obj[] array,String ip){
        int low=0;
        int hight=array.length-1;
        while (low<=hight)
        {
            int middle=(low+hight)/2;
            if((ip.compareTo(array[middle].startIp)>=0)&&(ip.compareTo(array[middle].endIp)<=0))
            {
                return middle;
            }
            if (ip.compareTo(array[middle].startIp) < 0)
                hight = middle - 1;
            else {
                low = middle + 1;
            }
        }
        return -1;
    }

    private class Invalid{
        private Invalid()
        {

        }
    }
}

Debug the UDF locally

  1. Open the data file at warehouse/example_project/__tables__/wc_in2/p1=2/p2=1/data in your local project.

  2. Add up to three IPv4 addresses from ipv4.txt in the last column of the data file, then save.

  3. Right-click the UDF script and select Run.

  4. In the Run/Debug Configurations dialog box, configure the required parameters and click OK. If no error is returned, the code ran successfully and you can proceed. If an error is reported, troubleshoot based on the error information in IntelliJ IDEA. For more information, see Perform a local run to debug the UDF.

    The parameter settings in the preceding figure are for reference only.

    Configure the required parameters

Step 4: Deploy the UDF

  1. Right-click the UDF script and select Deploy to server….

    Upload the UDF script

  2. In the Package a jar, submit resource and register function dialog box, configure the parameters. For more information about the parameters, see Package a Java program, upload the package, and create a MaxCompute UDF. Under Extra resources, select ipv4.txt and ipv6.txt — the file resources you uploaded in Step 1. In this tutorial, the function is named ipv4_ipv6_aton.

    Generate a JAR package and create a function

Step 5: Call the UDF

Start the MaxCompute client and run a SQL SELECT statement to call ipv4_ipv6_aton.

Convert an IPv4 address:

select ipv4_ipv6_aton('116.11.XX.XX');

Expected output:

Beihai, Guangxi Zhuang Autonomous Region

Convert an IPv6 address:

select ipv4_ipv6_aton('2001:0250:080b:0:0:0:0:0');

Expected output:

Baoding, Hebei Province

What's next