diff --git a/src/Umbraco.Core/IO/MediaFileSystem.cs b/src/Umbraco.Core/IO/MediaFileSystem.cs index e3d8f489c6..de795303af 100644 --- a/src/Umbraco.Core/IO/MediaFileSystem.cs +++ b/src/Umbraco.Core/IO/MediaFileSystem.cs @@ -1,8 +1,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; -using Umbraco.Core.CodeAnnotations; using Umbraco.Core.Configuration; namespace Umbraco.Core.IO @@ -27,6 +25,15 @@ namespace Umbraco.Core.IO return propertyId.ToString() + seperator + fileName; } + public string GetRelativePath(string subfolder, string fileName) + { + var seperator = UmbracoSettings.UploadAllowDirectories + ? Path.DirectorySeparatorChar + : '-'; + + return subfolder + seperator + fileName; + } + public IEnumerable GetThumbnails(string path) { var parentDirectory = System.IO.Path.GetDirectoryName(path); diff --git a/src/Umbraco.Core/Media/MediaSubfolderCounter.cs b/src/Umbraco.Core/Media/MediaSubfolderCounter.cs new file mode 100644 index 0000000000..6f0142cacf --- /dev/null +++ b/src/Umbraco.Core/Media/MediaSubfolderCounter.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Umbraco.Core.IO; + +namespace Umbraco.Core.Media +{ + /// + /// Internal singleton to handle the numbering of subfolders within the Media-folder. + /// When this class is initiated it will look for numbered subfolders and select the highest number, + /// which will be the start point for the naming of the next subfolders. If no subfolders exists + /// then the starting point will be 1000, ie. /media/1000/koala.jpg + /// + internal class MediaSubfolderCounter + { + #region Singleton + + private long _numberedFolder = 1000;//Default starting point + private static readonly ReaderWriterLockSlim ClearLock = new ReaderWriterLockSlim(); + private static readonly Lazy Lazy = new Lazy(() => new MediaSubfolderCounter()); + + public static MediaSubfolderCounter Current { get { return Lazy.Value; } } + + private MediaSubfolderCounter() + { + var folders = new List(); + var fs = FileSystemProviderManager.Current.GetFileSystemProvider(); + var directories = fs.GetDirectories(""); + foreach (var directory in directories) + { + long dirNum; + if (long.TryParse(directory, out dirNum)) + { + folders.Add(dirNum); + } + } + var last = folders.OrderBy(x => x).LastOrDefault(); + if(last != default(long)) + _numberedFolder = last; + } + + #endregion + + /// + /// Returns an increment of the numbered media subfolders. + /// + /// A value + public long Increment() + { + using (new ReadLock(ClearLock)) + { + _numberedFolder = _numberedFolder + 1; + return _numberedFolder; + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index 1cd26cfad6..a2815d04f8 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -11,6 +11,7 @@ using System.Xml; using System.Xml.Linq; using Umbraco.Core.Configuration; using Umbraco.Core.IO; +using Umbraco.Core.Media; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.UnitOfWork; @@ -142,16 +143,22 @@ namespace Umbraco.Core.Models /// /// Sets and uploads the file from a HttpPostedFileBase object as the property value /// - /// to add property value to + /// to add property value to /// Alias of the property to save the value on /// The containing the file that will be uploaded public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileBase value) { + // Ensure we get the filename without the path in IE in intranet mode + // http://stackoverflow.com/questions/382464/httppostedfile-filename-different-from-ie + var fileName = value.FileName; + if (fileName.LastIndexOf(@"\") > 0) + fileName = fileName.Substring(fileName.LastIndexOf(@"\") + 1); + var name = IOHelper.SafeFileName( - value.FileName.Substring(value.FileName.LastIndexOf(IOHelper.DirSepChar) + 1, - value.FileName.Length - value.FileName.LastIndexOf(IOHelper.DirSepChar) - 1) - .ToLower()); + fileName.Substring(fileName.LastIndexOf(IOHelper.DirSepChar) + 1, + fileName.Length - fileName.LastIndexOf(IOHelper.DirSepChar) - 1) + .ToLower()); if (string.IsNullOrEmpty(name) == false) SetFileOnContent(content, propertyTypeAlias, name, value.InputStream); @@ -160,16 +167,22 @@ namespace Umbraco.Core.Models /// /// Sets and uploads the file from a HttpPostedFile object as the property value /// - /// to add property value to + /// to add property value to /// Alias of the property to save the value on /// The containing the file that will be uploaded public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFile value) { + // Ensure we get the filename without the path in IE in intranet mode + // http://stackoverflow.com/questions/382464/httppostedfile-filename-different-from-ie + var fileName = value.FileName; + if (fileName.LastIndexOf(@"\") > 0) + fileName = fileName.Substring(fileName.LastIndexOf(@"\") + 1); + var name = IOHelper.SafeFileName( - value.FileName.Substring(value.FileName.LastIndexOf(IOHelper.DirSepChar) + 1, - value.FileName.Length - value.FileName.LastIndexOf(IOHelper.DirSepChar) - 1) - .ToLower()); + fileName.Substring(fileName.LastIndexOf(IOHelper.DirSepChar) + 1, + fileName.Length - fileName.LastIndexOf(IOHelper.DirSepChar) - 1) + .ToLower()); if (string.IsNullOrEmpty(name) == false) SetFileOnContent(content, propertyTypeAlias, name, value.InputStream); @@ -178,13 +191,33 @@ namespace Umbraco.Core.Models /// /// Sets and uploads the file from a HttpPostedFileWrapper object as the property value /// - /// to add property value to + /// to add property value to /// Alias of the property to save the value on /// The containing the file that will be uploaded public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileWrapper value) { - if (string.IsNullOrEmpty(value.FileName) == false) - SetFileOnContent(content, propertyTypeAlias, value.FileName, value.InputStream); + // Ensure we get the filename without the path in IE in intranet mode + // http://stackoverflow.com/questions/382464/httppostedfile-filename-different-from-ie + var fileName = value.FileName; + if (fileName.LastIndexOf(@"\") > 0) + fileName = fileName.Substring(fileName.LastIndexOf(@"\") + 1); + + if (string.IsNullOrEmpty(fileName) == false) + SetFileOnContent(content, propertyTypeAlias, fileName, value.InputStream); + } + + /// + /// Sets and uploads the file from a as the property value + /// + /// to add property value to + /// Alias of the property to save the value on + /// Name of the file + /// to save to disk + public static void SetValue(this IContentBase content, string propertyTypeAlias, string fileName, + Stream fileStream) + { + if (string.IsNullOrEmpty(fileName) == false && fileStream != null) + SetFileOnContent(content, propertyTypeAlias, fileName, fileStream); } private static void SetFileOnContent(IContentBase content, string propertyTypeAlias, string name, Stream fileStream) @@ -194,10 +227,12 @@ namespace Umbraco.Core.Models return; bool supportsResizing = false; + var numberedFolder = MediaSubfolderCounter.Current.Increment(); string fileName = UmbracoSettings.UploadAllowDirectories - ? Path.Combine(property.Id.ToString(), name) - : property.Id + "-" + name; - string extension = Path.GetExtension(name); + ? Path.Combine(numberedFolder.ToString(CultureInfo.InvariantCulture), name) + : numberedFolder + "-" + name; + + string extension = Path.GetExtension(name).Substring(1).ToLowerInvariant(); var fs = FileSystemProviderManager.Current.GetFileSystemProvider(); fs.AddFile(fileName, fileStream); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index b75df3d16e..5f6ed6a411 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -143,6 +143,7 @@ + diff --git a/src/umbraco.cms/businesslogic/datatype/FileHandlerData.cs b/src/umbraco.cms/businesslogic/datatype/FileHandlerData.cs index b19ca3f14a..481b9b7cb0 100644 --- a/src/umbraco.cms/businesslogic/datatype/FileHandlerData.cs +++ b/src/umbraco.cms/businesslogic/datatype/FileHandlerData.cs @@ -1,8 +1,10 @@ using System; +using System.Globalization; using System.IO; using System.Web; using System.Xml; using Umbraco.Core.IO; +using Umbraco.Core.Media; using umbraco.cms.businesslogic.Files; using umbraco.cms.businesslogic.property; @@ -55,9 +57,10 @@ namespace umbraco.cms.businesslogic.datatype // handle upload if (name != String.Empty) { + var numberedFolder = MediaSubfolderCounter.Current.Increment(); string fileName = UmbracoSettings.UploadAllowDirectories - ? Path.Combine(PropertyId.ToString(), name) - : PropertyId + "-" + name; + ? Path.Combine(numberedFolder.ToString(CultureInfo.InvariantCulture), name) + : numberedFolder + "-" + name; //fileName = Path.Combine(SystemDirectories.Media, fileName); UmbracoFile um = UmbracoFile.Save(fileStream, fileName); diff --git a/src/umbraco.cms/businesslogic/media/UmbracoImageMediaFactory.cs b/src/umbraco.cms/businesslogic/media/UmbracoImageMediaFactory.cs index 3c77e0cb5c..e4bd16d704 100644 --- a/src/umbraco.cms/businesslogic/media/UmbracoImageMediaFactory.cs +++ b/src/umbraco.cms/businesslogic/media/UmbracoImageMediaFactory.cs @@ -1,15 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Imaging; -using System.IO; -using System.Linq; -using System.Web; -using Umbraco.Core.Logging; -using umbraco.BasePages; +using System.Collections.Generic; +using Umbraco.Core.Models; using umbraco.BusinessLogic; -using umbraco.cms.businesslogic.datatype; namespace umbraco.cms.businesslogic.media { @@ -27,146 +18,17 @@ namespace umbraco.cms.businesslogic.media public override void DoHandleMedia(Media media, PostedMediaFile postedFile, User user) { - // Get Image object, width and height - var image = Image.FromStream(postedFile.InputStream); - var fileWidth = image.Width; - var fileHeight = image.Height; + // Set media property to upload the file as well as set all related properties + media.MediaItem.SetValue("umbracoFile", postedFile.FileName, postedFile.InputStream); - // Get umbracoFile property - var propertyId = media.getProperty("umbracoFile").Id; + // Copy back the values from the internal IMedia to ensure that the values are persisted when saved + foreach (var property in media.MediaItem.Properties) + { + media.getProperty(property.Alias).Value = property.Value; + } - // Get paths - var destFilePath = FileSystem.GetRelativePath(propertyId, postedFile.FileName); - var ext = Path.GetExtension(destFilePath).Substring(1); - - // Set media properties - media.getProperty("umbracoFile").Value = FileSystem.GetUrl(destFilePath); - - if (media.getProperty("umbracoWidth") != null) - media.getProperty("umbracoWidth").Value = fileWidth; - - if (media.getProperty("umbracoHeight") != null) - media.getProperty("umbracoHeight").Value = fileHeight; - - if (media.getProperty("umbracoBytes") != null) - media.getProperty("umbracoBytes").Value = postedFile.ContentLength; - - if (media.getProperty("umbracoExtension") != null) - media.getProperty("umbracoExtension").Value = ext; - - if (media.getProperty("umbracoExtensio") != null) - media.getProperty("umbracoExtensio").Value = ext; - - // Generate thumbnail - var thumbDestFilePath = Path.Combine(Path.GetDirectoryName(destFilePath), Path.GetFileNameWithoutExtension(destFilePath) + "_thumb"); - GenerateThumbnail(image, 100, fileWidth, fileHeight, ext, thumbDestFilePath + ".jpg"); - - // Generate additional thumbnails based on PreValues set in DataTypeDefinition uploadField - GenerateAdditionalThumbnails(image, fileWidth, fileHeight, ext, thumbDestFilePath); - - image.Dispose(); - - FileSystem.AddFile(destFilePath, postedFile.InputStream, postedFile.ReplaceExisting); - - // Save media + // Save media (using legacy media object to ensure the usage of the legacy events). media.Save(); } - - private void GenerateAdditionalThumbnails(Image image, int fileWidth, int fileHeight, string ext, string destFilePath) - { - var uploadFieldDataTypeId = new Guid("5032a6e6-69e3-491d-bb28-cd31cd11086c"); - - DataTypeDefinition dataTypeDef = null; - - try - { - // Get DataTypeDefinition of upload field - dataTypeDef = DataTypeDefinition.GetByDataTypeId(uploadFieldDataTypeId); - } - catch (Exception e) - { - LogHelper.Error("Could get Upload Field datatype definition", e); - } - - if (dataTypeDef != null) - { - // Get PreValues - var preValues = PreValues.GetPreValues(dataTypeDef.Id); - - var thumbnails = ""; - if (preValues.Count > 0) - thumbnails = ((PreValue)preValues[0]).Value; - - if (thumbnails != "") - { - var thumbnailSizes = thumbnails.Split(";".ToCharArray()); - foreach (var thumb in thumbnailSizes.Where(thumb => thumb != "")) - { - GenerateThumbnail(image, int.Parse(thumb), fileWidth, fileHeight, ext, destFilePath + "_" + thumb + ".jpg"); - } - } - } - } - - private void GenerateThumbnail(Image image, int maxWidthHeight, int fileWidth, int fileHeight, string ext, string thumbnailFileName) - { - // Generate thumbnailee - var fx = (float)fileWidth / maxWidthHeight; - var fy = (float)fileHeight / maxWidthHeight; - - // must fit in thumbnail size - var f = Math.Max(fx, fy); //if (f < 1) f = 1; - var widthTh = (int)Math.Round(fileWidth / f); - var heightTh = (int)Math.Round(fileHeight / f); - - // fixes for empty width or height - if (widthTh == 0) - widthTh = 1; - - if (heightTh == 0) - heightTh = 1; - - // Create new image with best quality settings - var bp = new Bitmap(widthTh, heightTh); - var g = Graphics.FromImage(bp); - g.SmoothingMode = SmoothingMode.HighQuality; - g.InterpolationMode = InterpolationMode.HighQualityBicubic; - g.PixelOffsetMode = PixelOffsetMode.HighQuality; - - // Copy the old image to the new and resized - var rect = new Rectangle(0, 0, widthTh, heightTh); - g.DrawImage(image, rect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel); - - // Copy metadata - var codecs = ImageCodecInfo.GetImageEncoders(); - - var codec = ext.ToLower() == "png" || ext.ToLower() == "gif" - ? codecs.Single(t => t.MimeType.Equals("image/png")) - : codecs.Single(t => t.MimeType.Equals("image/jpeg")); - - // Set compresion ratio to 90% - var ep = new EncoderParameters(); - ep.Param[0] = new EncoderParameter(Encoder.Quality, 90L); - - // Save the new image - if (codec != null) - { - var ms = new MemoryStream(); - bp.Save(ms, codec, ep); - ms.Seek(0, 0); - - FileSystem.AddFile(thumbnailFileName, ms); - - ms.Close(); - } - else - { - // Log error - LogHelper.Warn("Multiple file upload: Can't find appropriate codec"); - } - - bp.Dispose(); - g.Dispose(); - } } }