Uploading Photos, Videos and Raw Files

Zenfolio service supports two ways of uploading photos from the user's computer, both of them based on the HTTP POST request. The first method is the standard form-based file upload as described in RFC 1867. The second method uses a simplified POST request that is often easier to implement than a form-based upload.

Regardless of the method, the first step in uploading is to obtain the upload URL. Photos and videos are always uploaded into an existing gallery and the upload URL is specific to a gallery. To obtain the upload URL, obtain the PhotoSet snapshot with the LoadPhotoSet method. The upload URL for photos is returned in the UploadUrl field of the snapshot, the upload URL for videos can be found in the VideoUploadUrl field, and the upload URL for Raw files can be found in the RawUploadUrl field.

Note that the user need to opt in to using the Raw Storage feature before Raw files can be uploaded. See Raw Storage Opt-in section below for more detail.

When using form-based HTTP POST, the returned URL can be used "as is". When using the simplified POST method, a few additional parameters need to be added to the URL to describe the photo being uploaded.

Using the Form-Based HTTP POST

When uploading files with a POST request, you need to prepare post data in the multipart/form-data format and submit it in the request body. As the name suggests, the data in this format consists of several parts, one of which is the actual file data. Below is an example of a form-based POST request.

POST /demouser/p340540780/upload.ushx HTTP/1.1<cr><lf>
Host: up.zenfolio.com<cr><lf>
User-Agent: Upload Sample Application<cr><lf>
Content-Type: multipart/form-data; boundary=8a47a5f4dc58498da616<cr><lf>
Content-Length: 1290606<cr><lf>
X-Zenfolio-Token: 6cA3p2kbk1MvGplnQ-NgGtaKf1B3PaDPqOOCORpR/cnyAAKoeyGz=<cr><lf>
Content-Disposition: form-data; name="file"; filename="test.jpg"<cr><lf>
Content-Type: image/jpeg<cr><lf>
[binary file data]<cr><lf>
Content-Disposition: form-data; name="file_modified"<cr><lf>
Fri, 28 Jan 2005 13:15:04 GMT<cr><lf>

As you can see, the request body consists of two parts. The top part, which has to be named "file", contains the file data. The content type of this part should be set accordingly to the actual file type (image/jpeg in this example). The original file name has to be provided in the Content-Disposition header of this part.

The bottom part is optional. If present, it has to be named "file_modified" and it should contain the file modification time in the RFC 1123 format. If this part is omitted, the server tries to get the image modification date from the image metadata. However, if the file has no metadata, this part is the only way a server can know the file modification date, so it is advisable to include it.

Here are some tips and tricks regarding form-based POST requests:

  • Line endings are important. Each line ending is encoded as a sequence of carriage return and line feed charactes (ASCII codes 13 and 10 correspondingly), which are represented in the above example as <cr> and <lf>. You have to include no more and no less line endings than required by the standard. Specifically, note that one CR-LF pair immediately follows the binary file data and another one follows the final boundary string – these two are commonly overlooked.
  • The Content-Length header has to be included in the request. The Zenfolio server will reject the request if this header is not provided. Keep in mind that the value of Content-Length is the entire body length that includes all parts, not only the file data.
  • The X-Zenfolio-Token header provides the authentication token the application received as a result of user authentication. This header needs to be included, so the server can verify that the user is the gallery owner.

Using the Simplified HTTP POST

With the simplified HTTP POST, file data constitute the entire request body and any additional information is passed as query string parameters. Below is an example of a simplified POST request (note that the first line of the request is wrapped for readability):

POST /demouser/p340540780/upload.ushx?filename=test.jpg
    &modified=Fri,+28+Jan+2005+13%3A15%3A04+GMT HTTP/1.1<cr><lf>
Host: up.zenfolio.com<cr><lf>
User-Agent: Upload Sample Application<cr><lf>
Content-Type: image/jpeg<cr><lf>
Content-Length: 1290221<cr><lf>
X-Zenfolio-Token: 6cA3p2kbk1MvGplnQ-NgGtaKf1B3PaDPqOOCORpR/cnyAAKoeyGz=<cr><lf>
[binary file data]

Here are the important differences between the simplified POST requests and the form-based requests:

  • The entire request body is consumed by the file data, therefore the value of the Content-Length header should be set to the file size in bytes.
  • The Content-Type header should be set to the actual image MIME type.
  • Additional parameters are provided in the query string. The filename parameter specifies the original file name and the modified parameter specified the file modification time.

With both the form-based and the simplified POST requests, server response consists of a single line that specifies the identifier of the photo, video, or gallery file just uploaded, for example:

HTTP/1.1 200 Uploaded<cr><lf>
Content-Type: text/plain<cr><lf>
Content-Length: 11<cr><lf>

If upload fails, the server returns a standard or a non-standard HTTP response code. The following table lists non-standard HTTP response codes that can be returned by the Zenfolio server:




Video duration limit is exceeded


User storage quota is exceeded


The photo file size is greater than allowed by user's subscription plan


The photo dimensions are too big


The file format is invalid or not supported


The photo file size exceeds maximum value supported

Raw Storage Opt-in

Users need to opt in to using the Raw Storage feature before Raw files can be uploaded. In Zenfolio Edit View, this happens when a Raw file is first selected for uploading.

Applications can determine whether the user has already opted in for the Raw Storage feature by analyzing the RawStorageOptedIn field of the User snapshot. Note this field is only returned as part of the private profile, so the application needs to use the LoadPrivateProfile method to obtain the snapshot.

If RawStorageOptedIn is set to true, no further action is necessary and Raw files can be uploaded right away. Otherwise, the user should be navigated to the URL provided in the RawStorageOptInUrl field of the User snapshot. This will send the user to a Zenfolio page where they can read the terms and agree to using the feature.

Once the user returns to the application, the application should load user's private profile again to confirm that RawStorageOptedIn is now set to true.