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还不明白是什么意思,只有用到时在研究了。