A good practice to handle user upload has been shared in this post on the google security blog: It explains common pitfalls and some additional defense steps. Let me summarize here:
Encode your output for its context:
Make sure you have proper output encoding with different algorithm for the context. You should have different sanitization practices for each HTML, HTML attributes, JSON etc.
Serve user content from a different domain:
First of all, it's always wise to serve user content from a different domain. Google uses
googleusercontent.com and you will surely find it a cheap investment in your security. The Same Origin Policy helps you here, since an XSS one the less-important domain cannot reach over to your main domain.