Android:上传图像而不丢失 Exif 数据

标签 android exif

在我们的应用程序中,用户多年来(大致)使用以下代码上传了数百万张图片:

BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(postFilePath, bmOptions);
Bitmap roughBitmap = BitmapFactory.decodeFile(postFilePath, bmOptions);

ByteArrayOutputStream stream = new ByteArrayOutputStream();

roughBitmap.compress(Bitmap.CompressFormat.JPEG, 70, stream);
InputStream fis = new ByteArrayInputStream(stream.toByteArray());

int fileSize = stream.toByteArray().length;
conn.setRequestProperty("Content-Length", Integer.toString(fileSize));
conn.setFixedLengthStreamingMode(fileSize);

...

if (fis != null) {
    byte[] buf = new byte[10240];

    int read;

    while ((read = fis.read(buf)) > 0) {
        os.write(buf, 0, read);
        totalBytesRead += read;
        if (uploadProgressListener != null) {
            try {
                uploadProgressListener.onBytesUploaded(read);
            } catch (Exception e) {
                Log.e(e);
            }
        }
    }

    fis.close();
}

最近我们发现需要保存上传图像的 Exif 数据。问题是压缩位图时图像 Exif 数据丢失。我想到了使用 ExifInterface 从原始文件中提取这些数据:

ExifInterface oldExif = new ExifInterface(postFilePath);
String value = oldExif.getAttribute(ExifInterface.TAG_DATETIME);

..然后将其添加到 InputStream fis 中,然后继续上传文件。问题是 ExifInterface 无法将 Exif 数据保存到 InputStream

图片上传到服务器时如何保留图片中的Exif数据?

这不是重复的: 只是为了更深入地阐明,我已经尝试通过使用此方法来使用建议的重复问题:

public static void copyExif(String originalPath, InputStream newStream) throws IOException {

    String[] attributes = new String[]
            {
                    ExifInterface.TAG_DATETIME,
                    ExifInterface.TAG_DATETIME_DIGITIZED,
                    ExifInterface.TAG_EXPOSURE_TIME,
                    ExifInterface.TAG_FLASH,
                    ExifInterface.TAG_FOCAL_LENGTH,
                    ExifInterface.TAG_GPS_ALTITUDE,
                    ExifInterface.TAG_GPS_ALTITUDE_REF,
                    ExifInterface.TAG_GPS_DATESTAMP,
                    ExifInterface.TAG_GPS_LATITUDE,
                    ExifInterface.TAG_GPS_LATITUDE_REF,
                    ExifInterface.TAG_GPS_LONGITUDE,
                    ExifInterface.TAG_GPS_LONGITUDE_REF,
                    ExifInterface.TAG_GPS_PROCESSING_METHOD,
                    ExifInterface.TAG_GPS_TIMESTAMP,
                    ExifInterface.TAG_MAKE,
                    ExifInterface.TAG_MODEL,
                    ExifInterface.TAG_ORIENTATION,
                    ExifInterface.TAG_SUBSEC_TIME,
                    ExifInterface.TAG_WHITE_BALANCE
            };

    ExifInterface oldExif = new ExifInterface(originalPath);
    ExifInterface newExif = new ExifInterface(newStream);

    if (attributes.length > 0) {
        for (int i = 0; i < attributes.length; i++) {
            String value = oldExif.getAttribute(attributes[i]);
            if (value != null)
                newExif.setAttribute(attributes[i], value);
        }
        newExif.saveAttributes();
    }
}

.. 但出现异常 java.io.IOException: ExifInterface does not support saving attributes for the current input.newExif.saveAttributes(); 之后因为我'我正在尝试将属性保存到 InputStream。我还能怎么做?

最佳答案

我的解决方案:

正如@amuttsch@CommonsWare 所建议的,我:

  1. 将缩放/压缩后的位图保存到临时文件
  2. 将exif从原文件复制到临时文件
  3. 将临时文件转换为字节数组并将其发送以上传

.. 然后我发现服务器在生成图像变体时再次剥离了 Exif :-P 但这是服务器人员现在正在努力纠正的另一个故事。

主要代码:

...
// Copy original Exif to scaledBitmap
String tempFilePath = getTempFilePath(postFilePath);
try {
    FileOutputStream out = new FileOutputStream(tempFilePath);
    scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 70, out);
    copyExif(postFilePath, tempFilePath);
} catch (Exception e) {
    e.printStackTrace();
}

// Get stream from temp (exif loaded) file
File tempFile = new File(tempFilePath);
byte[] byteFile = readFile(tempFile);
fis = new ByteArrayInputStream(byteFile);

// Remove the temp file
boolean deleted = tempFile.delete();

// Finalize
int fileSize = byteFile.length;
conn.setRequestProperty("Content-Length", Integer.toString(fileSize));
conn.setFixedLengthStreamingMode(fileSize);
...

getTempFilePath():

private String getTempFilePath(String filename) {
    String temp = "_temp";
    int dot = filename.lastIndexOf(".");
    String ext = filename.substring(dot + 1);

    if (dot == -1 || !ext.matches("\\w+")) {
        filename += temp;
    } else {
        filename = filename.substring(0, dot) + temp + "." + ext;
    }

    return filename;
}

copyExif():

public static void copyExif(String originalPath, String newPath) throws IOException {

    String[] attributes = new String[]
            {
                    ExifInterface.TAG_DATETIME,
                    ExifInterface.TAG_DATETIME_DIGITIZED,
                    ExifInterface.TAG_EXPOSURE_TIME,
                    ExifInterface.TAG_FLASH,
                    ExifInterface.TAG_FOCAL_LENGTH,
                    ExifInterface.TAG_GPS_ALTITUDE,
                    ExifInterface.TAG_GPS_ALTITUDE_REF,
                    ExifInterface.TAG_GPS_DATESTAMP,
                    ExifInterface.TAG_GPS_LATITUDE,
                    ExifInterface.TAG_GPS_LATITUDE_REF,
                    ExifInterface.TAG_GPS_LONGITUDE,
                    ExifInterface.TAG_GPS_LONGITUDE_REF,
                    ExifInterface.TAG_GPS_PROCESSING_METHOD,
                    ExifInterface.TAG_GPS_TIMESTAMP,
                    ExifInterface.TAG_MAKE,
                    ExifInterface.TAG_MODEL,
                    ExifInterface.TAG_ORIENTATION,
                    ExifInterface.TAG_SUBSEC_TIME,
                    ExifInterface.TAG_WHITE_BALANCE
            };

    ExifInterface oldExif = new ExifInterface(originalPath);
    ExifInterface newExif = new ExifInterface(newPath);

    if (attributes.length > 0) {
        for (int i = 0; i < attributes.length; i++) {
            String value = oldExif.getAttribute(attributes[i]);
            if (value != null)
                newExif.setAttribute(attributes[i], value);
        }
        newExif.saveAttributes();
    }
}

读取文件():

public static byte[] readFile(File file) throws IOException {
    // Open file
    RandomAccessFile f = new RandomAccessFile(file, "r");
    try {
        // Get and check length
        long longlength = f.length();
        int length = (int) longlength;
        if (length != longlength)
            throw new IOException("File size >= 2 GB");
        // Read file and return data
        byte[] data = new byte[length];
        f.readFully(data);
        return data;
    } finally {
        f.close();
    }
}

关于Android:上传图像而不丢失 Exif 数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44928092/

相关文章:

android - 停止 ScrollView 将焦点设置在 EditText

android - 尽管 ADB 成功检测到手机,但未检测到 USB 设备或正在运行的模拟器

iphone - iOS :Get UIImage Orientation inside a UIImageview with exif data?

iphone - 对于图像元数据,什么标签允许最大字符限制?

java - Android Camera打开然后在新的activity中打开拍摄的图片

Android 从所有 View 中移除焦点

java - 使用改造 2 在回收站 View 上显示项目

windows - 使用 EXIF 增加序列号

javascript - 变量似乎没有在移动设备上设置

testing - 需要非压缩 TIFF 格式的带有 exif 缩略图的 JPEG 测试图像