We've had some occasional bug reports that file uploads sometimes failed. Users would upload something like a profile photo, resume, or data file, and get this error when they went to view the file:
It turned out that we were incorrectly URL encoding file names. The file was correctly uploaded, but we returned an incorrect URL to access the file!
On the backend, we use Google Cloud's Signed URL feature to let users directly upload files to the cloud.
Here's how it works:
- Frontend makes a
UserFileUploadRequest request with the name of the file and the MIME-type
- The backend returns two URLs: a
ServingURL, which is the public link where the file can be accessed forever, and the
UploadURL, which is a temporary, signed link where the file can be uploaded.
- The frontend uploads the file to
- The file can be accessed at
Moonlight's API would immediately URL-encode the file name, then generate the Serving and Upload URLs. As we analyzed what was happening, we realized that the Google Cloud API was already URL-encoding the name of the file. So, our code ended up double-encoding the file for the UploadURL, but only single-encoding the ServingURL. So, any filenames that contained URL-unsafe characters would fail. Whoops!
Recommended code for Signed URLs on Google Cloud
We deployed the below fix and added some tests.
But wait, there's more!
When you upload a resume to Moonlight, we display it on your profile via the filename. We also updated this link tag component to URL-decode file names. So, links display better now 👍