如何更新照片上的时间戳或在 Google+ 张照片上添加时区?

How to update the timestamp on a photo or add the time zone on Google+ Photos?

使用 Picasa 网络 API 我从我的 Google+ 相册中检索了一张照片并尝试更改时间戳(我的 phone 上的时间有误,因此尝试修复它):

var service = new PicasaService("exampleCo-exampleApp-1");
service.setUserCredentials("uid", "pwd");
AlbumQuery query = new AlbumQuery(PicasaQuery.CreatePicasaUri("default"));

PicasaFeed feed = service.Query(query);
var entry = (PicasaEntry)feed.Entries.SingleOrDefault(f => f.Title.Text == "Trip to Italy - ALL");

var ac = new AlbumAccessor(entry);

var photoQuery = new PhotoQuery(PicasaQuery.CreatePicasaUri("default", ac.Id));
PicasaFeed photoFeed = service.Query(photoQuery);

PicasaEntry picasaEntry = photoFeed.Entries[0];

ulong timestamp = Convert.ToUInt64(picasaEntry.GetPhotoExtensionValue("timestamp"));

// deduct 9 hours
DateTime dt = FromUnixTime(pa.Timestamp).AddHours(-9);
picasaEntry.SetPhotoExtensionValue("timestamp", Convert.ToString(ToUnixTime(dt)));

var updatedEntry = (PicasaEntry) picasaEntry.Update();

不幸的是,虽然 .Update 方法成功了,但时间戳没有改变。 我还尝试更改照片的时区(例如,用户像这样手动执行相同的操作 http://i.imgur.com/pxYSi9S.png)。

我是不是漏掉了一些简单的东西? 还有另一种方法可以完成同样的事情吗?我也愿意改变照片的时区。

我亲自解决了你的问题,所以让我在这里分享我对情况的评估:

Google 似乎是从图像的Exif 标签中获取显示的元信息。 尽管在查看 .net API 时设置 Exif 标签似乎是可能的(我什至反编译了 Google 程序集,以确保一切正确),Google 只是不重新嵌入它们发布更新时进入图片。

所以我的方法是下载所有图像,更改它们的 Exif 信息并重新上传它们(如建议的 nemesv)。不幸的是 Google 确实删除了所有下载的文件的 Exif 标签(thx big G!)并用人工标签(例如应用程序名称 "Google")和空值(创建时间 = null)替换它们。 在 .net 中从头开始生成 Exif 信息充其量是骇人听闻的(必须强行构造 System.Drawing.Imaging.PropertyItem [具有内部构造函数,可以完成] 的实例并正确地参数化它们)。但是,由于我在我的主要工作的应用程序的成像模块中做了类似的事情(从现有图片中获取 Exif 信息,重新生成图片并将 Exif 重新添加到新图片),我认为这种方法是可行的。

这是为您提供的一些概念验证代码。它并没有完全解决(完整的解决方案应该读取现有 picasa 条目的日期并将它们缓存在列表中以将它们重新应用到下载的图像上),但它确实涵盖了棘手的部分。

private void button1_Click(object sender, EventArgs e)
{
    var service = new PicasaService("exampleCo-exampleApp-1");
    service.setUserCredentials("me.myself@gmail.com", "-secret-");
    AlbumQuery query = new AlbumQuery(PicasaQuery.CreatePicasaUri("default"));


    PicasaFeed feed = service.Query(query);
    var entry = (PicasaEntry)feed.Entries.SingleOrDefault(f => f.Title.Text == "Testalbum");

    var ac = new AlbumAccessor(entry);

    var photoQuery = new PhotoQuery(PicasaQuery.CreatePicasaUri("default", ac.Id));
    PicasaFeed photoFeed = service.Query(photoQuery);

    DirectoryInfo srcdir = Directory.CreateDirectory("C:\Temp\Testalbum");
    DownloadAllPhotos("C:\Temp\Testalbum", photoFeed.Entries);

    foreach (PicasaEntry oldentry in photoFeed.Entries)
    {
        oldentry.Delete();
    }

    DirectoryInfo tgtdir = Directory.CreateDirectory("C:\Temp\Converted");
    foreach (FileInfo imagefile in srcdir.EnumerateFiles())
    {
        Image img = Image.FromFile(imagefile.FullName);

        PropertyItem PiDtOrig = null;
        try
        {
            PiDtOrig = img.GetPropertyItem(0x9003); // id 0x9003 is "DateTimeOriginal"
        }
        catch (System.ArgumentException ex) // this exception is thrown when PropertyItem does not exist 
        {
            PiDtOrig = NewPropertyItem();
            PiDtOrig.Id = 0x9003;
            PiDtOrig.Type = 7;
            PiDtOrig.Len = 4;
        }

        PiDtOrig.Value = BitConverter.GetBytes(DateTimeToInt(DateTime.Now));
        img.SetPropertyItem(PiDtOrig);
        string ConvImgName = tgtdir.FullName + "\" + imagefile.Name;
        img.Save(ConvImgName);

        //ExifTagCollection exif = new ExifTagCollection(img);
        //Debug.WriteLine(exif);

        Uri postUri = new Uri(PicasaQuery.CreatePicasaUri("hommel.peter@gmail.com", ac.Id));
        FileStream fileStream = imagefile.OpenRead();

        PicasaEntry newentry = (PicasaEntry)service.Insert(postUri, fileStream, "image/jpeg", ConvImgName);

        fileStream.Close();
        fileStream.Dispose();
    }
}

private PropertyItem NewPropertyItem()
{
    Type t = typeof (PropertyItem);
    ConstructorInfo ctor = t.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)[0];
    Object o = ctor.Invoke(new Object[] { });
    return (PropertyItem) o;
}

private int DateTimeToInt(DateTime theDate)
{
    return (int)(theDate.Date - new DateTime(1900, 1, 1)).TotalDays + 2;
}

// taken from https://codethis.wordpress.com/2008/11/ and modified for this example
static void DownloadAllPhotos(string DirectoryName, AtomEntryCollection photoList)
{
    DirectoryInfo dirInfo = Directory.CreateDirectory(DirectoryName);

    int photoNum = 1;
    foreach (AtomEntry photo in photoList)
    {
        HttpWebRequest photoRequest = WebRequest.Create(photo.Content.AbsoluteUri) as HttpWebRequest;
        HttpWebResponse photoResponse = photoRequest.GetResponse() as
           HttpWebResponse;

        BufferedStream bufferedStream = new BufferedStream(
           photoResponse.GetResponseStream(), 1024);
        BinaryReader reader = new BinaryReader(bufferedStream);

        FileStream imgOut = File.Create(dirInfo.FullName + "\image" +
           photoNum++ + ".jpg");
        BinaryWriter writer = new BinaryWriter(imgOut);

        int bytesRead = 1;
        byte[] buffer = new byte[1024];
        while (bytesRead > 0)
        {
            bytesRead = reader.Read(buffer, 0, buffer.Length);
            writer.Write(buffer, 0, bytesRead);
        }
        reader.Close();
        reader.Dispose();
        writer.Flush();
        writer.Close();
        writer.Dispose();
    }
}

请耐心等待我发布有点脏的代码,但我很快就把它拼凑起来了。

你会在上面的代码中找到两行注释掉的代码,它们使用了一些我前段时间在网上找到的 Exif 类。由于他们直接在这里公开了太多代码,我已经将它们上传到Pastebin:

http://pastebin.com/pkZMVZ9i

虽然它们只允许读取 Exif,但在您尝试为其他 Exif 标签找到合适的 PropertyItem 内容时,它们仍然对您有用。

因为我不知道这段代码是从哪里来的,如果 he/she 有的话,请大家发表评论,这样我也可以在这里添加这些信息。