Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Created June 3, 2025 09:03
Show Gist options
  • Save sunmeat/3f914c2e37659b1d54e0161ef625c79d to your computer and use it in GitHub Desktop.
Save sunmeat/3f914c2e37659b1d54e0161ef625c79d to your computer and use it in GitHub Desktop.
react + php + files
создать папку files в папке react
=======================================================================================
App.jsx:
import { useState, useEffect } from 'react';
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';
import './App.css';
const API_URL = 'http://sunmeat.atwebpages.com/react/api.php';
const UPLOAD_URL = 'http://sunmeat.atwebpages.com/react/upload.php';
const queryClient = new QueryClient();
const truncateText = (text, maxLength) =>
text.length > maxLength ? text.slice(0, maxLength) + '...' : text;
function ProductList({ addToCart }) {
const fetchProducts = async () => {
const response = await fetch('https://fakestoreapi.com/products?limit=12');
if (!response.ok) throw new Error('Ошибка загрузки продуктов');
return response.json();
};
const { data, error, isLoading } = useQuery({
queryKey: ['products'],
queryFn: fetchProducts,
staleTime: 5 * 60 * 1000,
});
if (isLoading) return <div className="loading">Загрузка...</div>;
if (error) return <div className="error">Ошибка: {error.message}</div>;
return (
<div className="product-list">
<h2>Наши товары:</h2>
<div className="products">
{data.map((product) => (
<div key={product.id} className="product-card">
<img src={product.image} alt={product.title} className="product-image" />
<h3>{truncateText(product.title, 30)}</h3>
<p className="price">${product.price}</p>
<button onClick={() => addToCart(product)} className="add-to-cart">
Добавить в корзину
</button>
</div>
))}
</div>
</div>
);
}
function Cart({ cart, removeFromCart }) {
const total = cart.reduce((sum, item) => sum + Number(item.price), 0).toFixed(2);
return (
<div className="cart">
<h2>Корзина</h2>
{cart.length === 0 ? (
<p>Корзина пуста</p>
) : (
<div>
{cart.map((item) => (
<div key={item.id} className="cart-item">
<span>{truncateText(item.title, 30)}</span>
<span>${item.price}</span>
<button onClick={() => removeFromCart(item.id)} className="remove-from-cart">
Удалить
</button>
</div>
))}
<p className="total">Итого: ${total}</p>
</div>
)}
</div>
);
}
function App() {
const [cart, setCart] = useState([]);
const [error, setError] = useState(null);
const [uploadMessage, setUploadMessage] = useState(null);
useEffect(() => {
const loadCart = async () => {
try {
const response = await fetch(API_URL);
if (!response.ok) throw new Error('Ошибка загрузки корзины');
const data = await response.json();
if (data.error) throw new Error(data.error);
setCart(data);
} catch (err) {
setError(err.message);
}
};
loadCart();
}, []);
const addToCart = async (product) => {
try {
const newItem = {
id: product.id,
title: product.title,
price: Number(product.price),
image: product.image,
};
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newItem),
});
if (!response.ok) throw new Error('Ошибка добавления в корзину');
const result = await response.json();
if (result.error) throw new Error(result.error);
setCart((prevCart) => [
...prevCart,
{
...newItem,
product_id: newItem.id,
id: result.id,
},
]);
} catch (err) {
setError(err.message);
}
};
const removeFromCart = async (cartItemId) => {
try {
const response = await fetch(API_URL, {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: cartItemId }),
});
if (!response.ok) throw new Error('Ошибка удаления из корзины');
const result = await response.json();
if (result.error) throw new Error(result.error);
setCart((prevCart) => prevCart.filter((item) => item.id !== cartItemId));
} catch (err) {
setError(err.message);
}
};
const handleFileUpload = async (event) => {
const file = event.target.files[0];
if (!file) return;
// Validate file type (text or image)
const allowedTypes = [
'text/plain',
'image/jpeg',
'image/png',
'image/gif',
];
if (!allowedTypes.includes(file.type)) {
setUploadMessage('Ошибка: Допустимы только текстовые файлы (.txt) или изображения (.jpg, .png, .gif)');
return;
}
const maxSize = 5 * 1024 * 1024; // 5MB in bytes
if (file.size > maxSize) {
setUploadMessage('Ошибка: Файл слишком большой (максимум 5 МБ)');
return;
}
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch(UPLOAD_URL, {
method: 'POST',
body: formData,
});
const result = await response.json();
if (!response.ok || result.error) {
throw new Error(result.error || 'Ошибка загрузки файла');
}
setUploadMessage(`Файл "${file.name}" успешно загружен!`);
} catch (err) {
setUploadMessage(`Ошибка: ${err.message}`);
}
};
return (
<QueryClientProvider client={queryClient}>
<div className="app">
<h1>Интернет-магазин ReactExpress</h1>
{error && <div className="error">Ошибка: {error}</div>}
<div className="file-upload">
<h2>Загрузка файла</h2>
<input
type="file"
accept=".txt,image/jpeg,image/png,image/gif"
onChange={handleFileUpload}
/>
{uploadMessage && <p className={uploadMessage.includes('Ошибка') ? 'error' : 'success'}>{uploadMessage}</p>}
</div>
<ProductList addToCart={addToCart} />
<Cart cart={cart} removeFromCart={removeFromCart} />
</div>
</QueryClientProvider>
);
}
export default App;
=======================================================================================
пересобрать пример, npm run build, заменить файлы index.html и 2 файла в папке assets
=======================================================================================
react/upload.php:
<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit;
}
function logMessage($message) {
$timestamp = date('Y-m-d H:i:s');
$logEntry = "[$timestamp] $message\n";
file_put_contents('log.txt', $logEntry, FILE_APPEND | LOCK_EX);
}
$uploadDir = 'files/';
$baseUrl = 'http://sunmeat.atwebpages.com/react/files/';
if (!is_dir($uploadDir)) {
if (!mkdir($uploadDir, 0755, true)) {
logMessage("Ошибка создания директории: $uploadDir");
http_response_code(500);
echo json_encode(['error' => 'Ошибка создания директории для загрузок']);
exit;
}
}
if (!is_writable($uploadDir)) {
logMessage("Директория $uploadDir не доступна для записи");
http_response_code(500);
echo json_encode(['error' => 'Директория недоступна для записи']);
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
try {
if (!isset($_FILES['file'])) {
http_response_code(400);
echo json_encode(['error' => 'Файл не был загружен']);
exit;
}
$file = $_FILES['file'];
$allowedTypes = ['text/plain', 'image/jpeg', 'image/png', 'image/gif'];
$maxSize = 5 * 1024 * 1024; // 5MB
if (!in_array($file['type'], $allowedTypes)) {
http_response_code(400);
echo json_encode(['error' => 'Недопустимый тип файла. Допустимы: .txt, .jpg, .png, .gif']);
exit;
}
if ($file['size'] > $maxSize) {
http_response_code(400);
echo json_encode(['error' => 'Файл слишком большой (максимум 5 МБ)']);
exit;
}
if ($file['error'] !== UPLOAD_ERR_OK) {
logMessage("Ошибка загрузки файла: код ошибки " . $file['error']);
http_response_code(400);
echo json_encode(['error' => 'Ошибка загрузки файла']);
exit;
}
$fileName = uniqid() . '_' . basename($file['name']);
$filePath = $uploadDir . $fileName;
if (!move_uploaded_file($file['tmp_name'], $filePath)) {
logMessage("Ошибка перемещения файла в $filePath");
http_response_code(500);
echo json_encode(['error' => 'Ошибка сохранения файла']);
exit;
}
logMessage("Файл успешно загружен: $fileName");
echo json_encode([
'success' => true,
'fileName' => $fileName,
'fileUrl' => $baseUrl . $fileName,
'message' => 'Файл успешно загружен'
]);
} catch (Exception $e) {
logMessage("Ошибка обработки загрузки: " . $e->getMessage());
http_response_code(500);
echo json_encode(['error' => 'Ошибка сервера: ' . $e->getMessage()]);
}
} else {
logMessage("Неподдерживаемый метод: " . $_SERVER['REQUEST_METHOD']);
http_response_code(405);
echo json_encode(['error' => 'Метод не поддерживается']);
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment