Supports c3 build service = YES
Support platform
- Android = YES
- IOS = NO
- Browser = YES only webrct
Download addon Document file
Download addon WebRTC
Description plugin Document file
Action
If Android denies permission to access non-public file paths, such as files inside the WhatsApp folder, use the action "Copy file to cache."
Note: The action "Show overlay all audio list" may also be denied by Android if the clicked item comes from a non-public folder, even if permission has already been granted. In this case, also use the "Copy file to cache" action.
Important: The "Copy file to cache" action may consume significant device storage. If necessary, use the "Clear app old cache" action to free up space.
Condition
every action list is triggered if an error occurs, the condition is in the ON ERROR (COMBO) category.
then get the response from the Expression name Error message.
Expression
Upload file to server
Save this code as an upload.php file on your server. This script already handles CORS, validation, security, and generates the reply URL dynamically.
- This works immediately without changing anything
- or modify upload.php to your liking.
<?php
// ======================================================================
// 1. CORS HANDLING (MUST BE AT THE VERY TOP)
// ======================================================================
$origin = $_SERVER['HTTP_ORIGIN'] ?? '*';
header('Access-Control-Allow-Origin: ' . $origin);
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
header('Access-Control-Allow-Credentials: true');
// Respond to preflight requests
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit();
}
// ======================================================================
// 2. HELPER FUNCTIONS
// ======================================================================
/**
* Send a JSON response and terminate execution.
* @param bool $success Operation status.
* @param array $data Data to include in the response.
* @param int $httpCode HTTP status code.
*/
function sendJsonResponse(bool $success, array $data, int $httpCode = 200) {
http_response_code($httpCode);
header('Content-Type: application/json');
echo json_encode(['success' => $success] + $data);
exit();
}
/**
* Translate PHP file upload error codes into human-readable messages.
* @param int $errorCode The error code from $_FILES['file']['error'].
* @return string Error message.
*/
function handleUploadError(int $errorCode): string {
switch ($errorCode) {
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
return 'File size exceeds the limit.';
case UPLOAD_ERR_PARTIAL:
return 'The file was only partially uploaded.';
case UPLOAD_ERR_NO_FILE:
return 'No file was uploaded.';
case UPLOAD_ERR_NO_TMP_DIR:
return 'Missing a temporary folder on the server.';
case UPLOAD_ERR_CANT_WRITE:
return 'Failed to write file to disk.';
case UPLOAD_ERR_EXTENSION:
return 'A PHP extension stopped the file upload.';
default:
return 'An unknown upload error occurred.';
}
}
// ======================================================================
// 3. MAIN LOGIC WITH CENTRALIZED ERROR HANDLING
// ======================================================================
try {
// Ensure this is a POST request
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
throw new Exception('Only POST method is allowed.', 405);
}
// Validate required parameters
if (empty($_POST['userId']) || empty($_POST['extension']) || !isset($_FILES['file'])) {
throw new Exception('Missing required parameters (userId, extension, or file).', 400);
}
// Check for upload errors
if ($_FILES['file']['error'] !== UPLOAD_ERR_OK) {
throw new Exception(handleUploadError($_FILES['file']['error']), 500);
}
// Sanitize userId and extension from the client
$userId = preg_replace('/[^a-zA-Z0-9_-]/', '', $_POST['userId']);
// Create user directory if it doesn't exist
$mainUploadDir = 'uploads/';
$userUploadDir = $mainUploadDir . $userId . '/';
if (!is_dir($userUploadDir) && !mkdir($userUploadDir, 0755, true)) {
throw new Exception('Failed to create user directory. Check server permissions.', 500);
}
// Obtain file information from the server side (more reliable)
$originalFileName = basename($_FILES['file']['name']);
// Further sanitize the file name for security
$safeFileName = preg_replace('/[^a-zA-Z0-9-_.]/', '', $originalFileName);
$serverExtension = strtolower(pathinfo($safeFileName, PATHINFO_EXTENSION));
// ## CLIENT-SIDE EXTENSION VALIDATION REMOVED AS PER REQUEST ##
// File type validation (whitelist) remains for security
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'mp3', 'mp4', 'm4a', 'aac', 'webm'];
if (!in_array($serverExtension, $allowedExtensions)) {
throw new Exception('File type "' . $serverExtension . '" is not allowed.', 415);
}
// Validate file size
$maxFileSize = 20 * 1024 * 1024; // 20 MB
if ($_FILES['file']['size'] > $maxFileSize) {
throw new Exception('File size exceeds the 20MB limit.', 413);
}
// Generate a unique new file name
$newFileName = time() . '_' . $safeFileName;
$targetFilePath = $userUploadDir . $newFileName;
// Move the file to permanent location
if (!move_uploaded_file($_FILES['file']['tmp_name'], $targetFilePath)) {
throw new Exception('Failed to save the uploaded file on the server.', 500);
}
// If successful, send a success response in JSON format
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'];
$finalFileUrl = $protocol . '://' . $host . '/' . $targetFilePath;
sendJsonResponse(true, [
'message' => 'File uploaded successfully.',
'url' => $finalFileUrl,
'fileName' => $newFileName
]);
} catch (Exception $e) {
// Handle all errors in one place and send a JSON response
sendJsonResponse(false, ['error' => $e->getMessage()], $e->getCode());
}
Old documentation
Simple example c3p: Video Recording and Take Capture image
select file, upload, download, will return a response
Response (Success):
Object: A JSON object containing metadata of the successfully.
- uri (String): The content URI (content://) of the saved file.
- traditionalPath (String, optional): The traditional file system path (e.g., /storage/emulated/0/Music/test.mp3)
- name (String): The display name of the file.
- size (Number): The size of the file in bytes.
- sizeReadable (String): The file size in a human-readable format. (e.g., "1.23 MB").
- mimeType (String): The MIME type of the file.
- lastModified (Number): The last modified timestamp of the file in milliseconds.
- isDirectory (Boolean): true if it's a directory, false if it's a file.
- base64: Base64-encoded string from the contents of a stored or selected file, or from an upload/download response.
(provided that permission has been granted)
You can handle, manipulate, image, audio, video, pdf, and other files, whatever the user chooses on their device, to be rendered into HTML elements, or files processed by your own hosting server, or an AI server.
The response from the server can be directly downloaded to the user's device, or just rendered to the HTML element.
The upload, download or response file select will return metadata for use as needed, (provided permission has been granted),
If the user denies permission, the metadata will not be obtained, the process on the android device will fail.
all processes on the user's device must go through the SAF URI path, not the traditional file system path traditional file system path (e.g., /storage/emulated/0/Download/filename.pdf).
For certain categories of apps only
developer.android.com/training/data-storage/manage-all-files
- File managers
- Backup and restore apps
- Anti-virus apps
- Document management apps
- On-device file search
- Disk and file encryption
- Device-to-device data migration
Screenshot of example permissions (Video Recording and Take Capture image)
NOTE sensitive permissions