java curl开发记录(二) linux系统下jni封装curl,android java使用curl

libcurl主要功能就是用不同的协议连接和沟通不同的服务器~也就是相当封装了的sockPHP 支持libcurl(允许你用不同的协议连接和沟通不同的服务器)。, libcurl当前支持http, https, ftp, gopher, telnet, dict, file, 和ldap 协议。libcurl同样支持HTTPS证书授权,HTTP POST, HTTP PUT, FTP 上传(当然你也可以使用PHP的ftp扩展), HTTP基本表单上传,代理,cookies,以及用户认证。

PHP具备curl扩展,但是java却不具备curl功能。此文章介绍如何在linux系统下,通过jni封装curl,供java进行调用

在Ubuntu下面,首先安装libcurl库

sudo aptitude install libcurl4-openssl-dev

如果需要使用最新的libcurl库,那么就需要自行下载源码来编译安装了,为了简单,采用apt方式安装也没什么。

libcurl的API就不多做介绍了,初始化以及清理curl都是通过native来进行调用的,因此要进行一个封装

public Curl() {
		if (!INIT) {
			curlGlobalInit(CurlConstant.CURL_GLOBAL_DEFAULT);
		}
	}

	public static void curlGlobalInit(int flags) {
		if (INIT) {
			return;
		}

		CurlCode code = CurlCode.fromValue(curlGlobalInitNative(flags));
		if (code != CurlCode.CURLE_OK) {
			throw new IllegalStateException("curlGlobalInit fail: " + code);
		}
		INIT = true;
	}

	private native static int curlGlobalInitNative(int flags);

	private native static void curlGlobalCleanupNative();

	public void curlEasyInit() throws CurlException {
		// Log.v(TAG, "curlEastInit");
		handle = curlEasyInitNative();
		if (handle == 0) {
			throw new CurlException("curl init native fail");
		}
	}

	private native long curlEasyInitNative();

	public void curlEasyCleanup() {
		// Log.v(TAG, "curlEastCleanup: " + handle);
		if (handle != 0) {
			curlEasyCleanupNative(handle);
		}
		handle = 0;
	}

	private native void curlEasyCleanupNative(long handle);

然后需要对opt进行封装:

/**
	 * 
	 * @param opt
	 *            {@link OptLong}
	 * @param value
	 * @return
	 */
	public CurlCode curlEasySetopt(OptLong opt, long value) {
		// Log.v(TAG, "curlEastSetopt: " + opt + "=" + value);
		return CurlCode.fromValue(curlEasySetoptLongNative(handle,
				opt.getValue(), value));
	}

	private native int curlEasySetoptLongNative(long handle, int opt, long value);

	public CurlCode curlEasySetopt(OptFunctionPoint opt, WriteCallback callback) {
		// Log.v(TAG, "curlEastSetopt: " + opt + "=" + callback);
		return CurlCode.fromValue(curlEasySetoptFunctionNative(handle,
				opt.getValue(), callback));
	}

	private native int curlEasySetoptFunctionNative(long handle, int opt,
			Callback callback);

	public CurlCode curlEasySetopt(OptObjectPoint opt, String value) {
		// Log.v(TAG, "curlEastSetopt: " + opt + "=" + value);
		return CurlCode.fromValue(curlEasySetoptObjectPointNative(handle,
				opt.getValue(), value));
	}

	private native int curlEasySetoptObjectPointNative(long handle, int opt,
			String value);

	public CurlCode curlEasySetopt(OptObjectPoint opt, byte[] value) {
		// Log.v(TAG, "curlEastSetopt: " + opt + "=" + value);
		return CurlCode.fromValue(curlEasySetoptObjectPointBytesNative(handle,
				opt.getValue(), value));
	}

	private native int curlEasySetoptObjectPointBytesNative(long handle,
			int opt, byte[] value);

	public CurlCode curlEasySetopt(OptObjectPoint opt, String[] values) {
		// Log.v(TAG, "curlEastSetopt: " + opt + "=" + values);
		return CurlCode.fromValue(curlEasySetoptObjectPointArrayNative(handle,
				opt.getValue(), values));
	}

	private native int curlEasySetoptObjectPointArrayNative(long handle,
			int opt, String[] value);

对FormData以及Perform进行封装

/**
	 * 
	 * @param multiParts
	 * @return
	 */
	public CurlFormadd setFormdata(List multiParts) {
		if (multiParts != null && multiParts.size() > 0) {
			return CurlFormadd.fromValue(setFormdataNative(handle,
					multiParts.toArray(new MultiPart[multiParts.size()])));
		} else {
			return CurlFormadd.CURL_FORMADD_NULL;
		}
	}

	private native int setFormdataNative(long handle, MultiPart[] multiArray);

	public CurlCode curlEasyPerform() {
		return CurlCode.fromValue(curlEasyPerformNavite(handle));
	}

	private native int curlEasyPerformNavite(long handle);

所有的封装全部写到一个类里面,剩下要做的事情,就是通过javah生成头文件了

进入到bin/class目录

使用如下命令生成头文件

javah -o curldroid.h com.netbirdtech.libcurl.Curl

生成头文件之后,需要在使用C++来实现Curl.java中的native方法:

JNIEXPORT jint JNICALL Java_com_netbirdtech_libcurl_Curl_curlGlobalInitNative
  (JNIEnv * env, jclass cls, jint flag) {
    curl_global_init((int) flag);
}

JNIEXPORT void JNICALL Java_com_netbirdtech_libcurl_Curl_curlGlobalCleanupNative
  (JNIEnv * env, jclass cls) {
    curl_global_cleanup();
}

JNIEXPORT jlong JNICALL Java_com_netbirdtech_libcurl_Curl_curlEasyInitNative
  (JNIEnv * env, jobject obj) {
    CURL* curl = curl_easy_init();
    if (curl != 0) {
        Holder* holder = new Holder(curl);
        return (long) holder;
    }
    return 0;
}

JNIEXPORT void JNICALL Java_com_netbirdtech_libcurl_Curl_curlEasyCleanupNative
  (JNIEnv * env, jobject obj, jlong handle) {
    if (handle != 0) {
        Holder* holder = (Holder*) handle;
        curl_easy_cleanup(holder->getCurl());
        delete holder;
        holder = 0;
    }
}

JNIEXPORT jint JNICALL Java_com_netbirdtech_libcurl_Curl_curlEasySetoptLongNative
  (JNIEnv *env, jobject obj, jlong handle, jint opt, jlong value) {
    Holder* holder = (Holder*) handle;
    return (int) curl_easy_setopt(holder->getCurl(), (CURLoption) opt, (long) value);
}
JNIEXPORT int JNICALL Java_com_netbirdtech_libcurl_Curl_curlEasySetoptFunctionNative
  (JNIEnv * env, jobject obj, jlong handle, jint opt, jobject cb) {
    Holder* holder = (Holder*) handle;
    CURL * curl = holder->getCurl();
    jobject cb_ref = 0;
    switch (opt) {
    case CURLOPT_HEADERFUNCTION:
        //LOGV("setopt CURLOPT_HEADERFUNCTION");
        curl_easy_setopt(curl, (CURLoption) opt, &write_callback);
        cb_ref = env->NewGlobalRef(cb);
        holder->addGlobalRefs(cb_ref);
        curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)cb_ref);
        break;
    case CURLOPT_WRITEFUNCTION:
        // see http://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html
        curl_easy_setopt(curl, (CURLoption) opt, &write_callback);
        cb_ref = env->NewGlobalRef(cb);
        holder->addGlobalRefs(cb_ref);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)cb_ref);
        break;
    case CURLOPT_READFUNCTION:
        curl_easy_setopt(curl, (CURLoption) opt, &read_callback);
        cb_ref = env->NewGlobalRef(cb);
        holder->addGlobalRefs(cb_ref);
        curl_easy_setopt(curl, CURLOPT_READDATA, (void *)cb_ref);
        break;
    default:
        // no-op
        ;
    }
    return (int) CURLE_OK;
}

JNIEXPORT jint JNICALL Java_com_netbirdtech_libcurl_Curl_curlEasySetoptObjectPointNative
  (JNIEnv * env, jobject obj, jlong handle, jint opt, jstring value) {
    const char *str;
    int result;
    Holder* holder = (Holder*) handle;
    CURL * curl = holder->getCurl();
    jstring value_ref;
    str = env->GetStringUTFChars(value, 0);
    if (str == 0) {
       return 0;
    }

    result = (int) curl_easy_setopt(curl, (CURLoption) opt, str);
    switch(opt) {
    case CURLOPT_POSTFIELDS:
        // this field not copy data
        // see http://curl.haxx.se/libcurl/c/CURLOPT_POSTFIELDS.html
        value_ref = (jstring) env->NewGlobalRef(value);
        holder->addStringGlobalRefs(value_ref, str);
        break;
    default:
        // free
        env->ReleaseStringUTFChars(value, str);
    }

    return result;
}

JNIEXPORT jint JNICALL Java_com_netbirdtech_libcurl_Curl_curlEasySetoptObjectPointBytesNative
  (JNIEnv * env, jobject obj, jlong handle, jint opt, jbyteArray bytes) {
	int result;
	Holder* holder = (Holder*) handle;
	CURL * curl = holder->getCurl();

	jbyte* str = env->GetByteArrayElements(bytes, JNI_FALSE);
	int content_length = env->GetArrayLength(bytes);

	if (str == NULL) {
	   return 0;
	}

	result = (int) curl_easy_setopt(curl, (CURLoption) opt, str);
	switch(opt) {
	case CURLOPT_POSTFIELDS:
		// this field not copy data
		// see http://curl.haxx.se/libcurl/c/CURLOPT_POSTFIELDS.html
		// release after perform
		holder->addByteArrayGlobalRefs(env->NewGlobalRef(bytes), (const char*)str);
		break;
	default:
		// free
		env->ReleaseByteArrayElements(bytes, str, 0);
	}

	return result;
}

JNIEXPORT jint JNICALL Java_com_netbirdtech_libcurl_Curl_curlEasySetoptObjectPointArrayNative
  (JNIEnv *env, jobject obj, jlong handle, jint opt, jobjectArray values) {
    Holder* holder = (Holder*) handle;
    CURL * curl = holder->getCurl();

    const char *str;
    struct curl_slist *slist = 0;
    int nargs = env->GetArrayLength(values);
    for (int i = 0; i < nargs; i++) {
        jstring value = (jstring) env->GetObjectArrayElement(values, i);
        str = env->GetStringUTFChars(value, 0);
        if (str == 0) {
            return 0;
        }
        //LOGV("append slist");
        slist = curl_slist_append(slist, str);
        env->ReleaseStringUTFChars(value, str);
    }
    holder->addCurlSlist(slist);
    //LOGD("set slist option=%d, size=%d", opt, nargs);
    return curl_easy_setopt(curl, (CURLoption) opt, slist);
}

JNIEXPORT jint JNICALL Java_com_netbirdtech_libcurl_Curl_setFormdataNative
  (JNIEnv* env, jobject obj, jlong handle, jobjectArray multi_array) {
    Holder* holder = (Holder*) handle;
    if (holder == NULL) {
        return 0;
    }
    CURL* curl = holder->getCurl();

    struct curl_httppost* post = holder->getPost();;
    struct curl_httppost* last = NULL;
    // clear all
    if (post != NULL) {
        //LOGD("clear previous form.");
        curl_formfree(post);
        post = NULL;
    }

    if (multi_array != NULL) {
        CURLFORMcode code;
        int len = env->GetArrayLength(multi_array);
        //LOGD("set name/parts size=%d", len);
        for (int i = 0; i < len; i++) {
            //LOGV(".");
            jobject part = env->GetObjectArrayElement(multi_array, i);
            jstring name = (jstring) env->CallObjectMethod(part, MID_MultiPart_get_name);
            jstring filename = (jstring) env->CallObjectMethod(part, MID_MultiPart_get_filename);
            jstring content_type = (jstring) env->CallObjectMethod(part, MID_MultiPart_get_content_type);
            jbyteArray content = (jbyteArray) env->CallObjectMethod(part, MID_MultiPart_get_content);
            jbyte* bytes = env->GetByteArrayElements(content, 0);
            int content_length = env->GetArrayLength(content);

            holder->addByteArrayGlobalRefs(env->NewGlobalRef(content), (const char*)bytes); // release after perform

            const char* name_str = env->GetStringUTFChars(name, 0);

            // content_type and filename may be null
            if (content_type == NULL && filename == NULL) {
            	code = curl_formadd(&post, &last,
            						CURLFORM_COPYNAME, name_str,
            						CURLFORM_BUFFER, "file.dat",
            						CURLFORM_BUFFERPTR, bytes,
            						CURLFORM_BUFFERLENGTH, content_length,
								    CURLFORM_END);
            } else if (content_type == NULL) {
            	const char* filename_str = env->GetStringUTFChars(filename, 0);
            	code = curl_formadd(&post, &last,
									CURLFORM_COPYNAME, name_str,
									CURLFORM_BUFFER, filename_str,
									CURLFORM_BUFFERPTR, bytes,
									CURLFORM_BUFFERLENGTH, content_length,
									CURLFORM_END);
            	env->ReleaseStringUTFChars(filename, filename_str);
            } else if (filename == NULL) {
            	const char* content_type_str = env->GetStringUTFChars(content_type, 0);
            	code = curl_formadd(&post, &last,
									CURLFORM_COPYNAME, name_str,
									CURLFORM_BUFFER, "file.dat",
									CURLFORM_CONTENTTYPE, content_type_str,
									CURLFORM_BUFFERPTR, bytes,
									CURLFORM_BUFFERLENGTH, content_length,
									CURLFORM_END);
				env->ReleaseStringUTFChars(content_type, content_type_str);
            } else {
            	const char* filename_str = env->GetStringUTFChars(filename, 0);
            	const char* content_type_str = env->GetStringUTFChars(content_type, 0);
            	code = curl_formadd(&post, &last,
									CURLFORM_COPYNAME, name_str,
									CURLFORM_BUFFER, filename_str,
									CURLFORM_CONTENTTYPE, content_type_str,
									CURLFORM_BUFFERPTR, bytes,
									CURLFORM_BUFFERLENGTH, content_length,
									CURLFORM_END);
            	env->ReleaseStringUTFChars(filename, filename_str);
            	env->ReleaseStringUTFChars(content_type, content_type_str);
            }

            env->ReleaseStringUTFChars(name, name_str);
        }

        if (code != CURL_FORMADD_OK) {
        	////LOGW("curl_formadd error %d", code);
        	curl_formfree(post);
        	// TODO return fromadd error or setopt error?
        	return (int) code;
        }
    }

    if (post != NULL) {
    	//LOGV("set_opt CURLOPT_HTTPPOST");
		holder->setPost(post);
		return curl_easy_setopt(curl, CURLOPT_HTTPPOST, post);
    }
    return 0;
}


JNIEXPORT jint JNICALL Java_com_netbirdtech_libcurl_Curl_curlEasyPerformNavite
  (JNIEnv *env, jobject obj, jlong handle) {
    Holder* holder = (Holder*) handle;
    CURL * curl = holder->getCurl();
    return (int) curl_easy_perform(curl);
}


这个基本上得认真看看libcurl的API了


代码写好之后,就是进行编译了

为了方便,写了一个Makefile文件

jni:
    @echo "编译之前,请务必指定jni.h以及jni_md.h的实际运行目录"
    g++ curldroid.cpp -I /usr/lib/jdk/include -I /usr/lib/jdk/include/linux -lcurl  -fPIC -shared -o libcurl4java.so
install:
    @echo "执行install(安装so文件)操作之前,请务必指定java.library.path目录,通过System.getProperty(\"java.library.path\")可以获取"
    cp -f libcurl4java.so /usr/lib/libcurl4java.so
uninstall:
    @echo "执行uninstall(卸载so文件)操作之前,请务必指定java.library.path目录,通过System.getProperty(\"java.library.path\")可以获取"
    rm -f  /usr/lib/libcurl4java.so
clean:
    @echo "删除所有的链接文件以及所有的目标文件,可能需要root权限"
    rm -f *.o
    rm -f *.so
    rm -f /usr/lib/ibcurldroid.so


在linux系统下面,安装jdk的时候,务必要记下jdk的安装地址,需要指定jni.h的头文件路径

否则会编译失败。

执行make就可以生成so文件

执行make install之前修改install这个target的路径

cp -f libcurl4java.so /usr/lib/libcurl4java.so

如何获取已经说明了。

通过如下代码可以简单的测试功能:

	CurlResult result = CurlHttp.newInstance().addParam("ip", "202.108.67.57")
				.getUrl("http://ip.taobao.com/service/getIpInfo.php").perform();

		int status = result.getStatus();
		String statusLine = result.getStatusLine();
		String body = result.getBodyAsString();
		byte[] binaryData = result.getBody();
		String binaryStr = new String(binaryData, "UTF-8");
		// byte[] binaryDecodedDate = result.getDecodedBody(); // if gzipped
		String header = result.getHeader("ContentType"); // ignore header

		Map headers = result.getHeaders();
		System.out.println("status=" + status);
		System.out.println("statusLine=" + statusLine);
		System.out.println("body=" + body);
		System.out.println("binaryStr=" + binaryStr);
		System.out.println("header=" + header);
		for(int i=0;i

在android平台下,正确的编译了libcurl,同样能够在android上运行。


代码有空再上传,睡觉去咯






你可能感兴趣的:(java,curl-java)