当前位置: 首页 > 知识库问答 >
问题:

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

莫誉
2023-03-14

在我们的应用程序中,用户多年来一直在使用以下代码上传数百万张图像:

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中,然后继续上传文件。问题是< code>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不支持保存当前输入的属性。 我还能怎么做?

共有3个答案

姜彬郁
2023-03-14

资料来源:https://stackoverflow.com/a/11572752/8252521

回答者: https://stackoverflow.com/users/1592398/code-jaff

通过以下方式将文件转换为位图:

Bitmap bi = BitmapFactory.decode(filepath + "DSC00021.jpg");

您也可以指定选项,请查看 API 文档

或者如果您想将元数据从一个文件交换到另一个文件,sanselan可能是最佳选择。这在你操作图像时会很有帮助,例如调整大小。

示例代码将引导您朝正确的方向前进。

汪跃
2023-03-14

问题是压缩位图时图像Exif数据丢失

在读取位图时,EXIF数据丢失。位图没有EXIF标记。

当Exif数据上传到服务器时,如何将它们保留在图像中?

停止在位图中读取。只需按原样上传postFilePath的内容。它将包含它包含的任何EXIF标签。

我的假设是,您正在读取位图,希望以70%的JPEG质量再次保存它,从而节省大量带宽。我怀疑您没有节省太多,在某些情况下可能会增加带宽(例如,postFilePath指向PNG)。您的成本是大量的CPU时间、发生<code>OutOfMemoryError

相反,如果转换到70% JPEG是某种数据标准化方法,那么就在服务器上完成这项工作,因为服务器有更多的CPU能力、更多的磁盘空间、更多的RAM和持续的电源。

吕亮
2023-03-14

我的解决方案:

正如@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();
    }
}

readFile():

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();
    }
}
 类似资料:
  • 问题内容: 我需要使用Python调整jpg图像的大小,而又不丢失原始图像的EXIF数据(有关拍摄日期,相机型号等的元数据)。所有有关python和图像的google搜索都指向我当前正在使用的PIL库,但似乎无法保留元数据。我到目前为止(使用PIL)的代码是这样的: 有任何想法吗?还是我可能正在使用的其他库? 问题答案: http://www.emilas.com/jpeg/

  • 我正在开发一个Android应用程序,其中的一部分功能是用户可以从他们的设备中选择一个图像,将其与列表中的特定项目相关联,并在应用程序中使用它。我的目的是将图像本身存储在内存中,并将图像的路径与项目的其余存储信息一起存储在SQLite数据库中。 我已经能够让用户选择图像并上传,但图像有时会根据图片的拍摄方式进行旋转。我试着查看Exif数据,但方向总是为零。我曾尝试使用光标来查看信息,但也无济于事。

  • 我有以下代码: 美元这个- 没有出现警告/错误,提交成功,但我上传的图像不会显示在文件夹中/uploads/user/“我不知道为什么,我不知道如何检查我的上传路径是否正确。我希望有一些方向。但我确实用相同的代码从不同的函数上传了它的工作和图像显示。 我目前使用localhost。 表格如下:

  • 现在我有一个打开手机摄像头应用程序的意图,允许用户拍照,然后带着新图像回到我的应用程序。有了这个,它返回一个位图。为了获得图片的Uri,以便我可以将ImageView设置为它,我相信我必须先将其保存到存储。唯一的问题是当我的应用程序打开它时,图像质量非常差。在我必须压缩的部分,我保持了100的质量,所以我不确定我做错了什么。 以下是我如何启动照相机的意图: 以下是我如何处理它: 对于switchT

  • AndroidStudio 2021 SDK 30 我有一个可以使用GPS、共享等功能的网络视图,可以上传文件、询问相机或画廊。 当用户选择图库中的现有文件时,我的代码可以工作,但是当从相机中拍摄时,什么也不会发生 它还请求用户的常规权限 这是AndroidManifest。xml 主要活动 在网上阅读时,我发现了一些关于文件路径的东西。xml,但我尝试了不同的价值观,但没有成功 ........

  • 我正在尝试将一组照片上载到服务器,但请求失败。文件数组到达时总是显示为空。 req.body按预期显示数组。 图像通过Dropzone组件添加。(我尝试将其切换为标准输入,但它们似乎都以相同的方式传递文件) 图像被应用到FormData,名称图像文件在通过设置了多部分/表单数据标题的Axios POST请求发送之前被追加。 一旦传递到服务器,只有req.body似乎包含任何数据,req.files