Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding exif to files downloaded using reqwest and saved from bytes does not work. #53

Open
Bkmd100 opened this issue Jan 28, 2025 · 3 comments

Comments

@Bkmd100
Copy link

Bkmd100 commented Jan 28, 2025

When reading or writing metadata from files using this code

bytes= client.get(&url).send().await.unwrap().bytes().await.unwrap();
let mut dest = File::create(&save_path).await.unwrap();
dest.write_all(&bytes).await;

let image_path = std::path::Path::new(&save_path);
let mut metadata = Metadata::new();
 metadata.set_tag(
                ExifTag::ImageDescription(&description)
            );
 metadata.write_to_file(&image_path);

A panic and following error occurs:

thread 'main' panicked at  xxxx\little_exif-0.6.2\src\jpg.rs:59:41:
range end index 2 out of range for slice of length 0

I managed to bypass this by converting the bytes to an image first :

let img = image::load_from_memory(&content).unwrap();
let image_path = std::path::Path::new(&save_path);
img.save(image_path);
let image_path = std::path::Path::new(&save_path);
let mut metadata = Metadata::new();
 metadata.set_tag(
                ExifTag::ImageDescription(&description)
            );
 metadata.write_to_file(&image_path);

Also I would like to note that "Metadata::new_from_path" does not work in both cases, and files saved as bytes seems to be working fine in all apps.
Please ignore all the unwraps/async and mix of stdio/async_std.

@TechnikTobi
Copy link
Owner

Hi,

thanks for the issue! Could you please provide me with some example data to reproduce this? Ideally this would be the data as bytes that you are writing in dest.write_all(&bytes).await;

@Bkmd100
Copy link
Author

Bkmd100 commented Jan 28, 2025

Thanks for your quick reply, here is a full demo to easily copy/paste to replicate the problem

cargo :

[dependencies]
reqwest = { version = "0.12.9", features = ["full"] }
tokio = { version = "1.0.0", features = ["full"] }
async-std = "1.13.0"
image = "0.25.5"
little_exif ="0.6.2"

code :

use async_std::fs::{File};
use async_std::io::prelude::*;
use little_exif::exif_tag::ExifTag;
use little_exif::metadata::Metadata;

#[tokio::main]
async fn main() {
    let bytes_version = "image_bytes.jpeg";
    let image_version = "image.jpeg";
    let pic_url="https://picsum.photos/id/237/200/300";
    
    
    //let's fetch the picture
    let bytes = reqwest::get(pic_url)
        .await
        .unwrap()
        .bytes()
        .await
        .unwrap();


    //let's save it using image cargo
    let img = image::load_from_memory(&bytes).unwrap();
    let image_path = std::path::Path::new(image_version);
    img.save(image_path);

    //lets save it directly from the bytes
    let mut dest = File::create(bytes_version).await.unwrap();
    dest.write_all(&bytes);


    //create the metadata
    let mut metadata = Metadata::new();
    metadata.set_tag(ExifTag::ImageDescription("test".to_string()));

    //let us write to the regular version
    let metadata_image_path = std::path::Path::new(image_version);
    metadata.write_to_file(&metadata_image_path);//works fine

    //let us write to the bytes version
    let metadata_image_path = std::path::Path::new(bytes_version);
    metadata.write_to_file(&metadata_image_path); //should panic here
 
}


@TechnikTobi
Copy link
Owner

As far as I can tell this has to do something with the async write operation. Replacing

    //lets save it directly from the bytes
    let mut dest = File::create(bytes_version).await.unwrap();
    dest.write_all(&bytes);

with

    //lets save it directly from the bytes
    let mut dest = std::fs::OpenOptions::new()
        .write(true)
        .create(true)
        .truncate(true)
        .open(bytes_version)
        .unwrap();
    dest.write_all(&bytes);

(and adding use std::io::Write;) both options seem to work. From my current point of view this has something to do with the async write operation not being done when we want to perform the metadata.write_to_file operation. Now, I'm not an expert on async and await, so I guess you have a strong reason for preferring this in your code over the "classic" I/O-stuff?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants