Uploading Media to a Server

A photo goes up the way a browser form sends a file, as multipart/form-data. Countless React Native 36,878 examples append a plain object, form.append('cover', { uri, name, type }), which React Native's own networking reads from disk. BookNest tried exactly that, and the upload failed at once:

Output of 216
E ReactNativeJS: [Error: Uncaught (in promise, id: 0) Error: Unsupported FormDataPart
  implementation]

The message comes from expo/src/winter/fetch/convertFormData.ts. In SDK 57 the global fetch is Expo 6,418 's WinterCG-style implementation, whose FormData parts must be strings, Blobs, or objects with a bytes() method; the { uri } convention is React Native's alone. expo-file-system's File class provides bytes(), so after npx expo install expo-file-system and a rebuild, the upload became:

src/api/covers.ts: uploading a photo with expo-file-system's File (excerpt)TypeScript
import { File } from 'expo-file-system';
// ...
export async function uploadCover(uri: string) {
  const form = new FormData();
  form.append('cover', new File(uri));
  const res = await fetch(`${API}/covers`, { method: 'POST', body: form });
  if (!res.ok) throw new Error(`upload failed: ${res.status}`);
  return (await res.json()) as { bytes: number };
}

The practice API's new POST /covers only counts the bytes it receives. A photo from the camera and the picked back cover arrived as:

Output of 217
  multipart/form-data, 17093 bytes
20:35:23 POST /covers 201
  multipart/form-data, 55152 bytes
20:39:59 POST /covers 201

Resize before uploading (the picker's quality, or expo-image-manipulator), show progress for large files, and upload straight to object storage with a pre-signed URL rather than through your API server when files are big.