Android ExifInterface对JPG文件的exif信息进行读写

Android ExifInterface对JPG文件的exif信息进行读写

ExifInterface是android提供对JPG文件进行读取和写入exif信息的类,一般用手机拍照,JPG文件中的exif信息会包含这些,如:手机型号,厂商,图片拍摄时间,地理位置等等。

在不同的手机上可能会有些差异,同时每个TAG所保留的值的长度是不一样的,待会会通过MD5加密,看看每个TAG的长度,还有图片需要是SD卡目录,其他目录会包错,具体在代码中列出。(记得添加SD卡权限)

下面来看看代码吧:

布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:id="@+id/edit"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="输入添加到图片的exif信息" />

    <Button
        android:id="@+id/btn_set"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="设置并保存图片的Exif信息" />

    <Button
        android:id="@+id/btn_set_md5"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="设置并保存图片的Exif信息,使用MD5加密" />

    <Button
        android:id="@+id/btn_get"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="获取图片的Exif信息" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin" />

</LinearLayout>

MainActivity

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button get;
    private Button set;
    private ExifInterface exif;
    private List<String> mlists = new ArrayList<>();
    private RecyclerView recyclerView;
    private MyAdapter adapter;
    private String path;
    private EditText edit;
    private Button set_md5;
    private boolean encode;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        initData();
        initRecycler();
        initListener();
    }

    private void initView() {
        edit = (EditText) findViewById(R.id.edit);
        get = (Button) findViewById(R.id.btn_get);
        set = (Button) findViewById(R.id.btn_set);
        set_md5 = (Button) findViewById(R.id.btn_set_md5);
        recyclerView = (RecyclerView) findViewById(R.id.recycler);
    }

    private void initListener() {
        set.setOnClickListener(this);
        set_md5.setOnClickListener(this);
        get.setOnClickListener(this);
    }

    private void initData() {
        //路径是为了测试给图片添加exif信息所能接受的路径,[注:]记得加SD存储权限
        String sdPath = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/1.jpg";
        String sdCachePath = getExternalCacheDir().getPath() + "/1.jpg";//JHEAD: can't open '/storage/sdcard0/Android/data/com.zhang.exifdemo/cache/1.jpg'
        String dataCachePath = getCacheDir().getPath() + "/1.jpg";//JHEAD: can't open '/data/data/com.zhang.exifdemo/cache/1.jpg'

        //--------------测试结果表明,sdPath目录是可以对exif信息进行读写的,其他目录会报错如:/JHEAD: can't open/...------------------//

        //将SD卡中的照片写到sd卡缓存中
        imageIO(sdPath, sdCachePath);
        //将SD卡中的照片写到应用包名下的缓存中
        imageIO(sdPath, dataCachePath);

        path = sdPath;
        Log.e("zhang", " path-->" + path);

        try {
            exif = new ExifInterface(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void imageIO(String target, String desc) {
        if (!TextUtils.isGraphic(target) && !TextUtils.isEmpty(desc)) {
            File targetFile = new File(target);
            File descFile = new File(desc);
            OutputStream os = null;
            InputStream is = null;
            try {
                is = new FileInputStream(targetFile);
                os = new FileOutputStream(descFile);
                int len = -1;
                byte[] bytes = new byte[1024];
                while ((len = is.read(bytes)) != -1) {
                    os.write(bytes, 0, len);
                }
                os.flush();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (os != null) {
                        os.close();
                    }
                    if (is != null) {
                        is.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void initRecycler() {
        LinearLayoutManager manager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(manager);
        recyclerView.setHasFixedSize(true);
        adapter = new MyAdapter(getApplicationContext(), mlists);
        recyclerView.setAdapter(adapter);
    }

    public void setExifInfo() {
        String content = edit.getText().toString().trim();
        if (encode) {//md5加密内容,这么做的原因是,大概知道每个tag所能保存的信息的长度
            content = MD5Utils.md5(content);
        }
        if (!TextUtils.isEmpty(content)) {
            setExifStringInfo(content);
            save();
            finish();
        }
    }

    public void save() {
        try {
            exif.saveAttributes();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_get:
                getExifInfo();
                if (adapter != null) {
                    adapter.notifyDataSetChanged();
                } else {
                    adapter = new MyAdapter(this, mlists);
                }
                break;
            case R.id.btn_set:
                encode = false;
                setExifInfo();
                break;

            case R.id.btn_set_md5:
                encode = true;
                setExifInfo();
                break;
        }
    }

    public void getExifInfo() {
        if (mlists == null) {
            mlists = new ArrayList<>();
        } else {
            mlists.clear();
        }
        String make = exif.getAttribute(ExifInterface.TAG_MAKE);
        //jpg包含的exif常用信息
        mlists.add("TAG_MAKE:" + make);
        String model = exif.getAttribute(ExifInterface.TAG_MODEL);
        mlists.add("TAG_MODEL:" + model);
        String dateTime = exif.getAttribute(ExifInterface.TAG_DATETIME);
        mlists.add("TAG_DATETIME:" + dateTime);
        String artist = exif.getAttribute(ExifInterface.TAG_ARTIST);//API24,艺术家
        mlists.add("TAG_ARTIST:" + artist);
        String copyRight = exif.getAttribute(ExifInterface.TAG_COPYRIGHT);//API24,版权
        mlists.add("TAG_COPYRIGHT:" + copyRight);
        String version = exif.getAttribute(ExifInterface.TAG_EXIF_VERSION);//API24,Exif版本
        mlists.add("TAG_EXIF_VERSION:" + version);
//        int flash = exif.getAttributeInt(ExifInterface.TAG_FLASH, -1);//闪光灯
        String flash = exif.getAttribute(ExifInterface.TAG_FLASH);
        mlists.add("TAG_FLASH:" + flash);
        //相关经纬度信息
        String altitude = exif.getAttribute(ExifInterface.TAG_GPS_ALTITUDE);//海拔    Type is rational
        mlists.add("TAG_GPS_ALTITUDE:" + altitude);
        int altitudeRef = exif.getAttributeInt(ExifInterface.TAG_GPS_ALTITUDE_REF, 0);//海拔,单位m
        mlists.add("TAG_GPS_ALTITUDE_REF:" + altitudeRef);
        String gps_dataStamp = exif.getAttribute(ExifInterface.TAG_GPS_DATESTAMP);
        mlists.add("TAG_GPS_DATESTAMP:" + gps_dataStamp);
        String longitude = exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE);//经度  Type is rational. Format is "num1/denom1,num2/denom2,num3/denom3".
        mlists.add("TAG_GPS_LONGITUDE:" + longitude);
        String longitude_ref = exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF);//经度  E:表示东经
        mlists.add("TAG_GPS_LONGITUDE_REF:" + longitude_ref);
        String dest_longitude = exif.getAttribute(ExifInterface.TAG_GPS_DEST_LONGITUDE);// API24 //不知道干嘛用的
        mlists.add("TAG_GPS_DEST_LONGITUDE:" + dest_longitude);
        String dest_longitude_ref = exif.getAttribute(ExifInterface.TAG_GPS_DEST_LONGITUDE_REF);// API24 //不知道干嘛用的
        mlists.add("TAG_GPS_DEST_LONGITUDE_REF:" + dest_longitude_ref);
        String latitude = exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE);//纬度    同经度
        mlists.add("TAG_GPS_LATITUDE:" + latitude);
        String latitude_ref = exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF);//纬度    N表示北纬
        mlists.add("TAG_GPS_LATITUDE_REF:" + latitude_ref);
        String dest_latitude = exif.getAttribute(ExifInterface.TAG_GPS_DEST_LATITUDE);//不知道干嘛用的
        mlists.add("TAG_GPS_DEST_LATITUDE:" + dest_latitude);
        String dest_latitude_ref = exif.getAttribute(ExifInterface.TAG_GPS_DEST_LONGITUDE_REF);//不知道干嘛用的
        mlists.add("TAG_GPS_DEST_LONGITUDE_REF:" + dest_latitude_ref);

        //其他信息
        String user_comment = exif.getAttribute(ExifInterface.TAG_USER_COMMENT);//api24   用户自己添加的信息
        mlists.add("TAG_USER_COMMENT:" + user_comment);
    }

    private void setExifStringInfo(String s) {
        exif.setAttribute(ExifInterface.TAG_MAKE, s);
        exif.setAttribute(ExifInterface.TAG_MODEL, s);
        exif.setAttribute(ExifInterface.TAG_DATETIME, s);
        exif.setAttribute(ExifInterface.TAG_ARTIST, s);//API24,艺术家
        exif.setAttribute(ExifInterface.TAG_COPYRIGHT, s);//API24,版权
        exif.setAttribute(ExifInterface.TAG_EXIF_VERSION, s);//API24,Exif版本
        exif.setAttribute(ExifInterface.TAG_FLASH, s);

        exif.setAttribute(ExifInterface.TAG_GPS_ALTITUDE, s);//海拔    Type is rational
        exif.setAttribute(ExifInterface.TAG_GPS_ALTITUDE_REF, s);
        exif.setAttribute(ExifInterface.TAG_GPS_DATESTAMP, s);
        exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, s);//经度  Type is rational. Format is "num1/denom1,num2/denom2,num3/denom3".
        exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, s);//经度  E:表示东经
        exif.setAttribute(ExifInterface.TAG_GPS_DEST_LONGITUDE, s);// API24 //不知道干嘛用的
        exif.setAttribute(ExifInterface.TAG_GPS_DEST_LONGITUDE_REF, s);// API24 //不知道干嘛用的
        exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, s);//纬度    同经度
        exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, s);//纬度    N表示北纬
        exif.setAttribute(ExifInterface.TAG_GPS_DEST_LATITUDE, s);//不知道干嘛用的
        exif.setAttribute(ExifInterface.TAG_GPS_DEST_LONGITUDE_REF, s);//不知道干嘛用的
        //其他信息
        exif.setAttribute(ExifInterface.TAG_USER_COMMENT, s);//api24   用户自己添加的信息
    }
}

MyAdapter中的布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <TextView
        android:textColor="#333333"
        android:id="@+id/tv"
        android:layout_width="match_parent"
        android:layout_height="56dp" />
</LinearLayout>

MyAdapter

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private Context mContext;
    private List<String> mlists;

    public MyAdapter(Context applicationContext, List<String> lists) {
        mContext = applicationContext;
        mlists = lists;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.recycler_item, parent, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        MyViewHolder myViewHolder = (MyViewHolder) holder;
        myViewHolder.textView.setText(mlists.get(position));
    }

    @Override
    public int getItemCount() {
        return mlists == null ? 0 : mlists.size();
    }

    private class MyViewHolder extends RecyclerView.ViewHolder {
        TextView textView;

        public MyViewHolder(View itemView) {
            super(itemView);
            textView = (TextView) itemView.findViewById(R.id.tv);
        }
    }
}

MD5Utils工具类

public class MD5Utils {

    public static String md5(String pw) {
        try {
            // 拿到一个MD5转换器(如果想要SHA1参数换成”SHA1”)
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            // 输入的字符串转换成字节数组
            byte[] inputByteArray = pw.getBytes();
            // inputByteArray是输入字符串转换得到的字节数组
            messageDigest.update(inputByteArray);
            // 转换并返回结果,也是字节数组,包含16个元素
            byte[] resultByteArray = messageDigest.digest();
            // 字符数组转换成字符串返回
            return byteArrayToHex(resultByteArray);
        } catch (NoSuchAlgorithmException e) {
            return null;
        }
    }

    public static String byteArrayToHex(byte[] byteArray) {

        // 首先初始化一个字符数组,用来存放每个16进制字符
        char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        // new一个字符数组,这个就是用来组成结果字符串的(解释一下:一个byte是八位二进制,也就是2位十六进制字符(2的8次方等于16的2次方))
        char[] resultCharArray = new char[byteArray.length * 2];
        // 遍历字节数组,通过位运算(位运算效率高),转换成字符放到字符数组中去
        int index = 0;
        for (byte b : byteArray) {
            resultCharArray[index++] = hexDigits[b >>> 4 & 0xf];
            resultCharArray[index++] = hexDigits[b & 0xf];
        }
        // 字符数组组合成字符串返回
        return new String(resultCharArray);
    }
}

注:TAG太多,很多是API24才添加进去的,很多TAG还不明白是什么意思,只有用到时在研究了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值