16boke - 一路博客

JNI Windows平台(包、解决中文路径乱码、数组传入、传出)

以C++实现读写文件,Java调用为例。包括中文路径的传递和数组的传入传出。

1.环境准备

    保证Java代码能够正确编译运行(安装JDK,正确配置环境变量)

    保证C++代码能够正确编译(安装VS或者Eclipse等)

文件目录结构

    E:\JNI\com\yiluboke\zxy\WriteReadJ.java

    E:\JNI\WriteReadC++

2.编写java代码(WriteReadJ.java)

package com.yiluboke.zxy;
class WriteReadJ {
    // 函数功能:将内存数据写入文件
    // 传入参数:bData 内存数据
    // iLen 内存数据的长度
    // strFilePath 文件路径
    // 返回参数:
    public native int writeFile(byte bData[], String strFilePath);
    // 函数功能:文件是否正确读取
    // 传入参数:strFilePath 文件路径
    // 返回参数:0 正确
    public native int readFilePre(String strFilePath);
    // 函数功能:读取文件
    // 传入参数:strFilePath 文件路径
    // 返回参数:返回文件的内容
    public native byte[] readFile(String strFilePath);
    static {
        // 调用动态链接库
        System.loadLibrary("jniwritereadfile");
    }
    public static void main(String[] args){
        WriteReadJ demo = new WriteReadJ();
        byte[] bytes = new byte[3];
        bytes[0] = '7';
        bytes[1] = '8';
        bytes[2] = 'h';
        int nResult = 0;
        String strFilePathName = "E:\\JNI\\新建文本文档.txt";
        try {            
            strFilePathName = new String(strFilePathName.getBytes(),"gb2312");
        } catch (Exception ex) {
            System.out.println(ex);
            return;
        }
        nResult = demo.writeFile(bytes, strFilePathName);
        if (nResult == 0) {
            System.out.println("写入成功");
        } else {
            System.out.println("写入失败错误码:" + nResult);
        }
        nResult = demo.readFilePre(strFilePathName);
        if (nResult == 0) {
            System.out.println("读取成功");
            byte[] fileReadData = demo.readFile(strFilePathName);
            System.out.println("获取数据如下: ");
            for (int index = 0; index < fileReadData.length; index++) {
                System.out.println(fileReadData[index]);
            }
        } else {
            System.out.println("读取失败错误码:" + nResult);
        }
    }
}

3.编译Java代码,生成class文件

    在E:\JNI目录下

    javac E:\JNI\com\yiluboke\zxy\WriteReadJ.java,在该java文件所在目录下生成WriteReadJ.class文件。

    

4.生成.h文件

    在E:\JNI目录下

    javah -jni com.yiluboke.zxy.WriteReadJ,在当前目录下生成com_yiluboke_zxy_WriteReadJ.h文件


5.编写C++代码,并编译成dll文件

writeReadDo.h

#ifndef __WRITEREADDO__H
#define __WRITEREADDO__H
typedef unsigned char BYTE;
const int MAXLEN = 1024;
extern BYTE gbData[MAXLEN];
extern int gbLen;
//函数功能:将内存数据写入文件
//传入参数:bData 内存数据
//          iLen 内存数据的长度
//          strFilePath 文件路径
//返回参数: 0 成功 非0参见错误码
int writeFile(BYTE bData[],int iLen,const char* strFilePath);
//函数功能:读取写入的数据
//传入参数:strFilePath 文件路径
//返回参数:0 成功 非0参见错误码
int readFile(const char* strFilePath);
#endif

writeReadDo.cpp

#include "writeReadDo.h"
#include "fstream"
using namespace std;
int writeFile(BYTE bData[],int iLen,const char* strFilePath)
{
    fstream  fOut(strFilePath, ios::out | ios::binary);
    if(!fOut)
        return -1;
    fOut.write((const char*)&bData[0],iLen);
    fOut.close();
    return 0;
}
BYTE gbData[MAXLEN] = {};
int gbLen = 0;
int readFile(const char* strFilePath)
{
    ifstream fIn(strFilePath,ios::in | ios::binary);
    if(!fIn)
        return -1;
    fIn.seekg(0,ios_base::end);
    int bCount = fIn.tellg();
    fIn.seekg(0 , ios::beg);
    if (bCount < MAXLEN)
    {
        gbLen = bCount;
        fIn.read((char*)&gbData[0], bCount);
    }
    else
    {
        gbLen = MAXLEN;
        fIn.read((char*)&gbData[0], MAXLEN);
    }
    fIn.close();
    return 0;
}

com_yiluboke_zxy_WriteReadJ.cpp

#include "com_yiluboke_zxy_WriteReadJ.h"
#include "writeReadDo.h"
#include
static const char* encodeMode = "GB2312";
char* Jstring2CStr(JNIEnv* env, jstring jstr, int* charLen)
{
    char* chRet = NULL;
    jclass classStr = env->FindClass("java/lang/String");
    jstring jstrEncode = env->NewStringUTF(encodeMode);
    jmethodID methodId = env->GetMethodID(classStr, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray bArray = (jbyteArray)env->CallObjectMethod(jstr, methodId, jstrEncode);
    jsize bArrayLen = env->GetArrayLength(bArray);
    *charLen = bArrayLen + 1;
    jbyte* pByte = env->GetByteArrayElements(bArray,JNI_FALSE);
    if(bArrayLen > 0)
    {
        chRet =new char[bArrayLen + 1];
        memcpy(chRet,pByte,bArrayLen);
        chRet[bArrayLen]=0;
    }
    env->ReleaseByteArrayElements(bArray,pByte,0);
    return chRet;
}
JNIEXPORT jint JNICALL Java_com_yiluboke_zxy_WriteReadJ_writeFile
    (JNIEnv *env, jobject, jbyteArray bData, jstring jstrFilePath)
{
    int chlen = 0;
    const char* chFilePath = Jstring2CStr( env, jstrFilePath, &chlen) ;//jstringToWindows( env, jstrFilePath );  //(*env).GetStringUTFChars(jstrFilePath, 0); //修改文件名乱码
    jbyte *jData = (jbyte*)env->GetByteArrayElements(bData, 0);
    jsize  jSize = env->GetArrayLength(bData);
    BYTE* bytearr = (BYTE*)jData;
    int len = (int)jSize;
    int nResult = writeFile(bytearr,len, chFilePath);
    //(*env).ReleaseStringUTFChars(jstrFilePath, chFilePath);
    if(chFilePath)
    {
        delete [] chFilePath;
        chFilePath = NULL;
    }
    return nResult;
}
JNIEXPORT jint JNICALL Java_com_yiluboke_zxy_WriteReadJ_readFilePre
    (JNIEnv *env, jobject, jstring jstrFilePath)
{
    int chlen = 0;
    const char* chFilePath =Jstring2CStr( env, jstrFilePath, &chlen) ;// (*env).GetStringUTFChars(jstrFilePath, 0);//jstringToWindows(env,jstrFilePath);
    int nResult = readFile(chFilePath);
    //(*env).ReleaseStringUTFChars(jstrFilePath, chFilePath);
    if(chFilePath)
    {
        delete [] chFilePath;
        chFilePath = NULL;
    }
    return nResult;
}
JNIEXPORT jbyteArray JNICALL Java_com_yiluboke_zxy_WriteReadJ_readFile
    (JNIEnv *env, jobject, jstring)
{
    jbyte *by = (jbyte*)gbData;
    int len = gbLen;
    jbyteArray jarray = (*env).NewByteArray(len);
    (*env).SetByteArrayRegion(jarray, 0, len, by);
    return jarray;
}

6.运行java文件

    在E:\JNI目录下

    java com.yiluboke.zxy.WriteReadJ

    

7.完整Demo下载

    百度网盘地址:http://pan.baidu.com/s/1bot3wyr

    本文采 用的JDK是 64位版本1.8.0_60。

    E:\JNI>java -version

    java version "1.8.0_60"

    Java(TM) SE Runtime Environment (build 1.8.0_60-b27)

    Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)

编译错误:

    如果遇到编译错误jni.h错误,替换工程中jni.h及jni.md.h为电脑中JDK安装路径中的jni.h及jni_md.h即可。

运行错误:

    Exception in thread "main" java.lang.UnsatisfiedLinkError: E:\JNI\jniwritereadfile.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform

    dll编译成:64位dll

JNI  Java