Wednesday, 5 June 2013

Uploading (and Resizing) Images to SharePoint

Recently I blogged about a Picture Upload webpart I wrote, that allows administrative users the ability to upload user profile pictures to SharePoint on behalf of another user. That post is here, and the solution can be downloaded from the Microsoft TechNet Gallery, here.

Anyway, I thought I'd share a bit of the code here, specifically the part of uploading, resizing and saving an image to a SharePoint document library.

Getting a file to upload to SharePoint is very easy; essentially it involves dropping an asp:FileUpload control onto your webpart page, adding a button, and lastly (optionally) an asp:Image control to render out the image that was uploaded.



And after uploading the image...

 


The (visual) webpart page markup for this example looks like this;

<SharePoint:CssLink runat="server" ID="mycorestyles" DefaultUrl="/_layouts/incestyles/core.css"></SharePoint:CssLink>
<div class="container">
  <div class="row">
      <div class="span8 rowpad">
          <span>Click browse to locate a file to upload, then click Upload to save it to SharePoint.</span>
      </div>
  </div>
  <div class="row">
      <div class="span8 rowpad">
          <asp:Label ID="userMessage" runat="server" Text=""></asp:Label>
      </div>
  </div>
  <div class="row">
      <div class="span8 rowpad">
          <asp:FileUpload ID="fileBrowser" runat="server" />
          <asp:Button ID="uploadFile" runat="server" Text="Upload" OnClick="UploadFileClick" />
      </div>
  </div>
  <div class="row">
      <asp:Image ID="previewImage" runat="server" Visible="false" />

  </div>
</div> 

In the above markup, I've put a CSS reference to a stylesheet I'm using for formatting the page (incidentally, that stylesheet is based on Twitters Bootstrap).

In the uploadFile button's onclick event, we can then check a file has been selected, is in the right format and not too big, before finally uploading it to a memory stream. Once we have the memory steam, we can crop and/or resize the image to our requirements. In this example, we'll resize the image if it's width or height is greater than 300px.

To do all this, a couple of other methods are called to help, one of which is the ResizeImage method, that takes a memorystream, and the maximum width and maximum height values for the image.

In the below code I've omitted elevating permissions, error handling and validation to keep the example shorter.

protected void UploadFileClick(object sender, EventArgs e)
{
  try
  {
    userMessage.Text = String.Empty;
    SPWeb web = SPContext.Current.Site.RootWeb;
    previewImage.ImageUrl = null;
    previewImage.Visible = false;
    if (!fileBrowser.HasFile)
    {
      userMessage.Text = "Please select a user to associate this image with before clicking Upload.";
      return;
    }
    //Check the file is less than 4MB
    int fileSize = fileBrowser.PostedFile.ContentLength;
    if (fileSize > 4000000)
    {
      userMessage.Text += String.Format("File Size Exceeds 4MB. Choose a smaller file.");
      return;
    }

    String imageFileExtension = Path.GetExtension(fileBrowser.FileName);
    //Check the user has selected a jpg image
    if (imageFileExtension == null || imageFileExtension.ToLower() != ".jpg")
    {
      userMessage.Text += "The file you have selected is not in the right format. Please use a jpg image.";
      return;
    }

    var imageFileData = fileBrowser.FileBytes;
    using (var imageFileStream = new MemoryStream())
    {
      imageFileStream.Write(imageFileData, 0, imageFileData.Length);
      //Before uploading the image to SharePoint, lets make sure resize the image if the width or height are greater than 300px.
      var imagePreview = ResizeImage(imageFileStream, 300, 300);
      SPList listExists = web.Lists.TryGetList("picturelibrary");
      SPUtility.ValidateFormDigest();
      if (listExists == null)
      {
        //...
        return;
      }
      var v = web.AllowUnsafeUpdates;
      try
      {
        var fileName = fileBrowser.FileName.Replace(" ", "-");
        var urlpreview = String.Format("{0}/picturelibrary/{1}", web.Url, fileName);
        web.AllowUnsafeUpdates = true;
        web.Files.Add(urlpreview, imagePreview, true);
        previewImage.ImageUrl = urlpreview;
        previewImage.Visible = true;
      }
      catch (Exception exception)
      {
        //..Do something
      }
      finally
      {
        web.AllowUnsafeUpdates = v;
      }
    }
  }
  catch (Exception exception)
  {
    //...
  }
}

public byte[] ResizeImage(Stream fileData, int maxwidth, int maxheight)
{
  using (var image = new Bitmap(fileData))
  {
    int adjustedWidth = image.Width;
    int adjustedHieght = image.Height;
    
    //Check the image is less than the maxwidth. If not, resize the image dimensions.
    if (adjustedWidth > maxwidth)
    {
      decimal ratio = Decimal.Divide(maxwidth, adjustedWidth);
      adjustedWidth = maxwidth;
      adjustedHieght = Convert.ToInt32(Decimal.Multiply(adjustedHieght, ratio));
    }
    //Now that we've adjusted the width, check the hieght is below the maximum hieght value
    if (adjustedHieght > maxheight)
    {
      decimal ratio = Decimal.Divide(maxheight, adjustedHieght);
      adjustedHieght = maxheight;
      adjustedWidth = Convert.ToInt32(Decimal.Multiply(adjustedWidth, ratio));
    }

    var resizedImage = new Bitmap(adjustedWidth, adjustedHieght);
    var g = Graphics.FromImage(resizedImage);
    g.InterpolationMode = InterpolationMode.HighQualityBicubic;
    g.SmoothingMode = SmoothingMode.HighQuality;
    g.CompositingQuality = CompositingQuality.HighQuality;
    g.PixelOffsetMode = PixelOffsetMode.HighQuality;
    g.FillRectangle(Brushes.White, 0, 0, adjustedWidth, adjustedHieght);
    g.DrawImage(image, 0, 0, adjustedWidth, adjustedHieght);
    var ms = new MemoryStream();
    const int quality = 90;
    var encoderParameters = new EncoderParameters(1);
    encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, (long)quality);
    resizedImage.Save(ms, GetImageCodeInfo("image/jpeg"), encoderParameters);
    ms.Position = 0;
    var data = new byte[ms.Length];
    ms.Read(data, 0, (int)ms.Length);
    return data;
  }
}

public static ImageCodecInfo GetImageCodeInfo(string mimeType)
{
  ImageCodecInfo[] imageEncoders = ImageCodecInfo.GetImageEncoders();
  foreach (ImageCodecInfo imageCodeInfo in imageEncoders)
  {
    if (imageCodeInfo.MimeType.Equals(mimeType, StringComparison.OrdinalIgnoreCase))
      return imageCodeInfo;
  }
  return null;
}



5 comments:

  1. Do you have this WSP available where it just saves the image and profile information to a "picturelibrary" without going through with updating profile information? I'd like it to fire off an approval workflow.

    I'm not a dev so I don't have visual studio to modify code and repackage.

    ReplyDelete
    Replies
    1. Hi Peter. Yeah I could do that. I'll have a look at modifying it over the weekend and I'll put the wsp file on the TechNet Gallery. I'll post a link to it once it's done.

      Delete
    2. Thanks a lot. I'll watch the TechNet Gallery for the update. If you have any questions, just ask. Nothing too crazy, just an extra property in the webpart to tell it what location to upload too. And the picture file name can be the user account that was specified.

      Delete
  2. IT IS REALLY COOL, THANKS A LOT

    ReplyDelete