private void TryAlternateMethodsOfPersistingMetadata(string sourceFileName, string outputFileName, MetadataItemName metaName, MetaPersistAction persistAction)
        {
            // Three alternate attempts to persist the metadata:
            // 1. Use outputFileName parameter and a cloned copy of the file's metadata
            // 2. Use outputFileName parameter and the original file's metadata
            // 3. Rename the file and try again using a cloned copy of the file's metadata
            // Adapted from: https://code.google.com/p/flickrmetasync/source/browse/trunk/FlickrMetadataSync/Picture.cs?spec=svn29&r=29
            bool tryOneLastMethod = false;

            using (Stream originalFile = new FileStream(sourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                const BitmapCreateOptions createOptions = BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile;
                BitmapDecoder original = BitmapDecoder.Create(originalFile, createOptions, BitmapCacheOption.None);

                var output = new JpegBitmapEncoder();

                if (original.Frames[0] != null && original.Frames[0].Metadata != null)
                {
                    BitmapMetadata bitmapMetadata = original.Frames[0].Metadata.Clone() as BitmapMetadata;
                    bitmapMetadata.SetQuery("/app1/ifd/PaddingSchema:Padding", MetadataPaddingInBytes);
                    bitmapMetadata.SetQuery("/app1/ifd/exif/PaddingSchema:Padding", MetadataPaddingInBytes);
                    bitmapMetadata.SetQuery("/xmp/PaddingSchema:Padding", MetadataPaddingInBytes);

                    SetMetadata(bitmapMetadata, metaName, persistAction);

                    output.Frames.Add(BitmapFrame.Create(original.Frames[0], original.Frames[0].Thumbnail, bitmapMetadata, original.Frames[0].ColorContexts));
                }

                try
                {
                    using (Stream outputFile = File.Open(outputFileName, FileMode.Create, FileAccess.ReadWrite))
                    {
                        output.Save(outputFile);
                    }
                }
                catch (Exception e) //System.Exception, NotSupportedException, InvalidOperationException, ArgumentException
                {
                    if (e is NotSupportedException || e is ArgumentException)
                    {
                        output = new JpegBitmapEncoder();

                        output.Frames.Add(BitmapFrame.Create(original.Frames[0], original.Frames[0].Thumbnail, original.Metadata, original.Frames[0].ColorContexts));

                        using (Stream outputFile = File.Open(outputFileName, FileMode.Create, FileAccess.ReadWrite))
                        {
                            output.Save(outputFile);
                        }

                        tryOneLastMethod = true;
                    }
                    else
                    {
                        throw new Exception("Error saving picture.", e);
                    }
                }
            }

            if (tryOneLastMethod)
            {
                File.Move(outputFileName, outputFileName + "tmp");

                using (Stream recentlyOutputFile = new FileStream(outputFileName + "tmp", FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    const BitmapCreateOptions createOptions = BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile;
                    BitmapDecoder original = BitmapDecoder.Create(recentlyOutputFile, createOptions, BitmapCacheOption.None);
                    JpegBitmapEncoder output = new JpegBitmapEncoder();
                    if (original.Frames[0] != null && original.Frames[0].Metadata != null)
                    {
                        BitmapMetadata bitmapMetadata = original.Frames[0].Metadata.Clone() as BitmapMetadata;
                        bitmapMetadata.SetQuery("/app1/ifd/PaddingSchema:Padding", MetadataPaddingInBytes);
                        bitmapMetadata.SetQuery("/app1/ifd/exif/PaddingSchema:Padding", MetadataPaddingInBytes);
                        bitmapMetadata.SetQuery("/xmp/PaddingSchema:Padding", MetadataPaddingInBytes);

                        SetMetadata(bitmapMetadata, metaName, persistAction);

                        output.Frames.Add(BitmapFrame.Create(original.Frames[0], original.Frames[0].Thumbnail, bitmapMetadata, original.Frames[0].ColorContexts));
                    }

                    using (Stream outputFile = File.Open(outputFileName, FileMode.Create, FileAccess.ReadWrite))
                    {
                        output.Save(outputFile);
                    }
                }
                File.Delete(outputFileName + "tmp");
            }
        }
        private void SetMetadata(BitmapMetadata bitmapMetadata, MetadataItemName metaName, MetaPersistAction persistAction)
        {
            if (!UpdatableMetaItems.ContainsKey(metaName))
            {
                throw new ArgumentException(String.Format("This function does not support persisting the meta item {0}.", metaName));
            }

            switch (metaName)
            {
                case MetadataItemName.Orientation:
                    SetOrientationMetadata(bitmapMetadata, metaName, persistAction);
                    break;

                default:
                    throw new InvalidEnumArgumentException(String.Format(CultureInfo.CurrentCulture, "This function is not designed to handle the enumeration value {0}. The function must be updated.", metaName));
            }

            //if (caption != null)
            //{
            //	bitmapMetadata.Comment = caption;
            //}

            //if (dateTaken.HasValue)
            //{
            //	bitmapMetadata.DateTaken = dateTaken.Value.ToString("M/d/yyyy HH:mm:ss");
            //	bitmapMetadata.SetQuery(DATE_TAKEN_QUERY, dateTaken.Value.ToString("yyyy:MM:dd HH:mm:ss"));
            //	bitmapMetadata.SetQuery(DIGITIZED_DATE_QUERY, dateTaken.Value.ToString("yyyy:MM:dd HH:mm:ss"));
            //	bitmapMetadata.SetQuery(ORIGINAL_DATE_QUERY, dateTaken.Value.ToString("yyyy:MM:dd HH:mm:ss"));
            //}

            ////-----------tags----------------------
            //List<String> tagsList = new List<String>();

            //foreach (string tag in tags)
            //{
            //	if (tag.Length > 0)
            //		tagsList.Add(tag);
            //}

            //if (tagsList.Count == 0)
            //	tagsList.Add("");

            ////XMP
            //bitmapMetadata.Keywords = new System.Collections.ObjectModel.ReadOnlyCollection<string>(tagsList);

            ////IPTC
            //string[] iptcTagsList = tagsList.ToArray();
            //bitmapMetadata.SetQuery(IPTC_KEYWORDS_QUERY, iptcTagsList);
            ////-----------tags----------------------
        }
        private void SetOrientationMetadata(BitmapMetadata bitmapMetadata, MetadataItemName metaName, MetaPersistAction persistAction)
        {
            switch (persistAction)
            {
                case MetaPersistAction.Delete:
                    bitmapMetadata.RemoveQuery(UpdatableMetaItems[metaName]);
                    break;

                case MetaPersistAction.Save:
                    IGalleryObjectMetadataItem orientationMeta;
                    if (GalleryObject.MetadataItems.TryGetMetadataItem(metaName, out orientationMeta))
                    {
                        ushort orientationRaw;
                        if (UInt16.TryParse(orientationMeta.RawValue, out orientationRaw) && MetadataEnumHelper.IsValidOrientation((Orientation)orientationRaw))
                        {
                            bitmapMetadata.SetQuery(UpdatableMetaItems[metaName], orientationRaw);
                        }
                    }
                    break;

                default:
                    throw new InvalidEnumArgumentException(String.Format(CultureInfo.CurrentCulture, "This function is not designed to handle the enumeration value {0}. The function must be updated.", persistAction));
            }
        }
        /// <summary>
        /// Persists the meta value.
        /// </summary>
        /// <param name="metaName">Name of the meta.</param>
        /// <param name="persistAction">The persist action.</param>
        private void PersistMetaValue(MetadataItemName metaName, MetaPersistAction persistAction)
        {
            if (!UpdatableMetaItems.ContainsKey(metaName))
            {
                EventController.RecordEvent(String.Format("This version of Gallery Server Pro does not support modifying the meta value {0} in the original file. The request to save or delete the meta value was ignored.", metaName));
                return;
            }

            lock (_sharedLock)
            {
                bool isSuccessful = false;
                var filePath = GalleryObject.Original.FileNamePhysicalPath;

                using (Stream savedFile = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite))
                {
                    var output = BitmapDecoder.Create(savedFile, BitmapCreateOptions.None, BitmapCacheOption.Default);
                    var bitmapMetadata = output.Frames[0].CreateInPlaceBitmapMetadataWriter();

                    if (bitmapMetadata != null)
                    {
                        SetMetadata(bitmapMetadata, metaName, persistAction);

                        if (bitmapMetadata.TrySave())
                        {
                            isSuccessful = true;
                        }
                    }
                }

                // If the save wasn't successful, try to save another way.
                if (!isSuccessful)
                {
                    string tmpFilePath = Path.Combine(AppSetting.Instance.TempUploadDirectory, String.Concat(Guid.NewGuid().ToString(), ".tmp"));

                    TryAlternateMethodsOfPersistingMetadata(filePath, tmpFilePath, metaName, persistAction);

                    ReplaceFileSafely(tmpFilePath, filePath);
                }
            }
        }