Monday, May 31, 2010

How to add photos to user profiles programmatically in SharePoint 2010 RTM

Recently I got a task to upload some photos directly into SharePoint 2010 user profile store. After some research, I found the post How to upload a user profile photo programmatically  (by Péter Holpár). It's really good work, and basically works but need a little bit change in my case.



For some reason, I cannot get "ProfileImagePicker.LargeThumbnailSize" definition with Visual Studio 2010 RTM and SharePoint 2010 RTM. I think the code from Péter Holpár is based on beta version.

So I used the integer 128, 96 and 64 to replace LargeThumbnailSize, MediumThumbnailSize and SmallThumbnailSize.

Another problem is, "MethodInfo mi_CreateThumbnail = profileImagePickerType.GetMethod("CreateThumbnail", BindingFlags.NonPublic | BindingFlags.Static);" doesn't work. This is weird. I don't see the point for Microsoft to hide it from developers. Anyway, it's easy to get walkaround.

        public SPFile CreateThumbnail(Bitmap original, int idealWidth, int idealHeight, SPFolder objSPFolder, string strFileName)
        {
            SPFile objSPFile = null;

            System.Drawing.Image pThumbnail = original.GetThumbnailImage(idealWidth, idealHeight, null, new IntPtr());
            using (MemoryStream objMemoryStream = new MemoryStream())
            {
                pThumbnail.Save(objMemoryStream, System.Drawing.Imaging.ImageFormat.Jpeg);
                objSPFile = objSPFolder.Files.Add(strFileName, objMemoryStream.ToArray(), true);
            }

            return objSPFile;
        }

After that, we need to update the user profile property "PictureURL".


        objUserProfile[PropertyConstants.PictureUrl].Value = strPictureUrl;
        objUserProfile.Commit();

The variable strPictureUrl should be something like: "http://SharePointServer/my/User%20Photos/Profile%20Pictures/DomainName_UserLoginName_MThumb.jpg"

SharePoint 2010 will automatically choose the file with the correct size in different place. If you go to the user profile page, you will see the image file "DomainName_UserLoginName_LThumb.jpg" is displayed.

Updated at 07/07/2010:


Below is my code to upload photos "manually". So if you don't want to implement the functionality through reflector, you can do it this way.


        private string UploadPhoto(string accountName, string imageFilePath, SPFolder subfolderForPictures, ProfileImagePicker profileImagePicker)
        {
            string strPictureUrl = string.Empty;
            SPFile objSPFilePhoto = null;

            if (!File.Exists(imageFilePath) || Path.GetExtension(imageFilePath).Equals(".gif"))
            {
                writeLog(string.Format("File '{0}' does not exist or has invalid extension", imageFilePath));
            }
            else
            {
                string fileNameWithoutExtension = null;
                string charsToReplace = @"\/:*?""<>|";
                Array.ForEach(charsToReplace.ToCharArray(), charToReplace => accountName = accountName.Replace(charToReplace, '_'));
                fileNameWithoutExtension = accountName;

                string jpgExtension = ".jpg";

                try
                {
                    SPSecurity.RunWithElevatedPrivileges(delegate()
                    {
                        if (subfolderForPictures != null)
                        {
                            // try casting length (long) to int
                            byte[] buffer = File.ReadAllBytes(imageFilePath);
                            using (MemoryStream stream = new MemoryStream(buffer))
                            {
                                using (Bitmap bitmap = new Bitmap(stream, true))
                                {
                                    //0x20 small
                                    //0x60 medium
                                    //0x90 large
                                    //writeLog(string.Format("LThumb = {0}", fileNameWithoutExtension + @"_LThumb" + jpgExtension));
                                    CreateThumbnail(bitmap, 0x90, 0x90, subfolderForPictures, fileNameWithoutExtension + @"_LThumb" + jpgExtension);
                                    objSPFilePhoto = CreateThumbnail(bitmap, 0x60, 0x60, subfolderForPictures, fileNameWithoutExtension + @"_MThumb" + jpgExtension);
                                    strPictureUrl = objSPFilePhoto.Web.Site.Url + @"/" + objSPFilePhoto.Url;
                                    CreateThumbnail(bitmap, 0x20, 0x20, subfolderForPictures, fileNameWithoutExtension + @"_SThumb" + jpgExtension);
                                    //objSPFilePhoto = CreateThumbnail(bitmap, bitmap.Width, bitmap.Height, subfolderForPictures, fileNameWithoutExtension + jpgExtension);
                                    writeLog(string.Format("strPictureUrl = {0}", strPictureUrl));
                                }
                            }
                        }
                    });
                }
                catch (Exception ex)
                {
                    writeLog("ex.Message = " + ex.Message);
                    writeLog("ex.StackTrace = " + ex.StackTrace);
                }
            }

            return strPictureUrl;
        }

3 comments:

  1. Thanks For the post.
    I am also using Péter Holpár code and couldnot find below function
    FieldInfo fi_m_objWeb = profileImagePickerType.GetField("m_objWeb", BindingFlags.NonPublic | BindingFlags.Instance);
    fi_m_objWeb.SetValue(profileImagePicker, web);

    MethodInfo mi_LoadPictureLibraryInternal = profileImagePickerType.GetMethod("LoadPictureLibraryInternal", BindingFlags.NonPublic | BindingFlags.Instance);

    did you face same issues?

    ReplyDelete
  2. For me
    MethodInfo mi_LoadPictureLibraryInternal = profileImagePickerType.GetMethod("LoadPictureLibraryInternal", BindingFlags.NonPublic | BindingFlags.Instance);
    doesn't work. Do you have some Walkarround for this also?

    ReplyDelete
  3. To neha:

    Thanks for your comments.

    I added more source code to the post. That code works well on both the test server and the production server. Hopefully that can save you some time.

    ReplyDelete