使用 PHP 将图像上传到文件夹,同时将描述保存到数据库
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6694839/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using PHP to upload images to a folder while saving descriptions to a database
提问by Mark Hatting
The user decides to upload multiple images for their classified listing. What I need to have happen is to:
用户决定为他们的分类列表上传多张图片。我需要发生的是:
- Upload the images with descriptions for each of the images - SOLVED
- Save the images to a folder specified
- Save the image location to my MySQL database with descriptions - SOLVED
- Be able to call the images and descriptions in their classified listing in kind of a gallery setting
- 上传带有每个图像描述的图像 - 已解决
- 将图像保存到指定的文件夹
- 使用描述将图像位置保存到我的 MySQL 数据库 - 已解决
- 能够以画廊设置的形式调用其分类列表中的图像和描述
My table schema is set up like this (simplified):
我的表架构是这样设置的(简化):
ad_id | member_id | category | subcategory | ... | photo_0_href | photo_0_desc ... | etc.
Could someone walk me through the process? Thanks.
有人可以指导我完成整个过程吗?谢谢。
step3.php
步骤3.php
<form action="upload.php" method="post" enctype="multipart/form-data">
<p>
<label for="file0">Filename: </label>
<input name="file[]" type="file" id="file0" size="20" />
</p>
<p>
<label for="file0desc">Description: </label>
<textarea rows="10" cols="30" id="file0desc" class="textarea"></textarea>
</p>
<p>
<label for="file1">Filename: </label>
<input name="file[]" type="file" id="file1" size="20" />
</p>
<p>
<label for="file1desc">Description: </label>
<textarea rows="10" cols="30" id="file1desc" class="textarea"></textarea>
</p>
<p>
<input id="submit" type="submit" name="submit" value="Continue to Step 4" />
</p>
</form>
upload.php
上传.php
<?php
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/pjpeg")
|| ($_FILES["file"]["type"] == "image/png"))
&& ($_FILES["file"]["size"] < 1048600)) // less than 1MB
{
if ($_FILES["file"]["error"] > 0)
{
echo "Return Code: " . $_FILES["file"]["error"] . "<br />";
}
else
{
header("Location: step4.php");
/*
echo "Upload: " . $_FILES["file"]["name"] . "<br />";
echo "Type: " . $_FILES["file"]["type"] . "<br />";
echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br />";
*/
if (file_exists("upload/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload/" . $_FILES["file"]["name"]);
echo "Stored in: " . "upload/" . $_FILES["file"]["name"];
}
}
}
else
{
echo "Invalid file";
}
?>
I know my upload.php isn't configured for multiple image files yet, but it's a start.
我知道我的 upload.php 还没有为多个图像文件配置,但这是一个开始。
I worked out saving the descriptions to the database using $_SESSION['file_n_desc']
. I just need to figure out how to upload the darn images to the folder and then save the locations into the database.
我想出了将描述保存到数据库中的方法$_SESSION['file_n_desc']
。我只需要弄清楚如何将 darn 图像上传到文件夹,然后将位置保存到数据库中。
I also need to have the image renamed to a random string (to prevent images being overwritten). I know I can do this with the rand()
function.
我还需要将图像重命名为随机字符串(以防止图像被覆盖)。我知道我可以用这个rand()
函数做到这一点。
回答by outis
1) Uploading files
1) 上传文件
When using array syntax for file inputs, the file index is the last key. $_FILES["file"]["name"]
, for example, is an array of file names. To get the information for the i-th file, you'll need to access $_FILES["file"]["name"][$i]
, $_FILES["file"]["size"][$i]
&c.
对文件输入使用数组语法时,文件索引是最后一个键。$_FILES["file"]["name"]
例如,是一个文件名数组。要获取第 i 个文件的信息,您需要访问$_FILES["file"]["name"][$i]
, $_FILES["file"]["size"][$i]
&c。
2) Saving Images to a Folder
2) 将图像保存到文件夹
Some of the data in $_FILES
(such as the name) comes from the client, and thus is not to be trusted (that is, verify first). In the case of the file name, you could start by using realpath
to verify the target file pathname is safe, or use basename
or pathinfo
to extract the last component of the supplied name before assembling the target pathname.
中的一些数据$_FILES
(例如名称)来自客户端,因此不可信任(即先验证)。在file name的情况下,您可以首先使用realpath
来验证目标文件路径名是否安全,或者在组合目标路径名之前使用basename
或pathinfo
提取所提供名称的最后一部分。
3) Saving image info to DB
3) 将图像信息保存到数据库
The (incomplete) database schema you give looks like you give each image two columns in the same table. Under the relational model, to-many relationships are modeled with a separate table:
您提供的(不完整的)数据库架构看起来就像您在同一个表中为每个图像提供了两列。在关系模型下,对多关系使用单独的表进行建模:
CREATE TABLE images (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
`path` VARCHAR(256) NOT NULL,
`description` TEXT,
`member` INT UNSIGNED NOT NULL,
FOREIGN KEY `member` REFERENCES members (`id`) ON DELETE CASCADE ON UPDATE CASCADE -- the image's owner
) Engine=InnoDB;
-- Note: this is a many-to-many relationship
CREATE TABLE ad_images (
`ad` INT UNSIGNED NOT NULL,
`image` INT UNSIGNED NOT NULL,
FOREIGN KEY `ad` REFERENCES ads (`ad_id`) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY `image` REFERENCES images (id) ON DELETE CASCADE ON UPDATE CASCADE,
UNIQUE KEY (`ad`, `image`)
) Engine=InnoDB;
Otherwise, you're breaking the zero-one-infinityrule and wasting space when there are fewer than the maximum number of images.
否则,当图像数量少于最大数量时,您将违反零一无穷大规则并浪费空间。
Other
其他
Note that you can use array syntax for the file description fields to make handling them easier. Name them "filedesc[]".
请注意,您可以对文件描述字段使用数组语法,以便更轻松地处理它们。将它们命名为“filedesc[]”。
Rather than a long sequence of comparisons, use an array lookup or a pattern match.
而不是一长串比较,使用数组查找或模式匹配。
function isImage($type) {
static $imageTypes = array(
'image/gif'=>1, 'image/jpeg'=>1, 'image/pjpeg'=>1, 'image/png'=>1,
);
return isset($imageTypes[$type]);
/* OR */
return preg_match('%^image/(?:p?jpeg|gif|png)%', $type);
/* OR allow all images */
return preg_match('%^image/%', $type);
}
if (isImage($_FILES["file"]["type"][$idx]) && ($_FILES["file"]["size"][$idx] < 1048600)) {
The file type is one of those client-supplied values. Safer would be to use fileinfo
to get the image type.
文件类型是客户端提供的值之一。更安全的是使用fileinfo
获取图像类型。
$finfo = finfo_open(FILEINFO_MIME_TYPE);
if (isImage(finfo_file($finfo, $path)) && ($_FILES["file"]["size"][$idx] < 1048600)) {
Even that can be fooled if the file has a valid image header but the rest isn't valid. You can use an image library(such as GD or ImageMagick) to validate the file by checking whether you can successfully open the file as an image.
如果文件具有有效的图像标题但其余部分无效,则即使这样也可以被愚弄。您可以使用图像库(例如 GD 或 ImageMagick)通过检查是否可以成功将文件作为图像打开来验证文件。
回答by jworrin
Not sure what errors you are having, as you seem to have the process pretty clear in your head. The first time I did this, i used this tutorial as a guide and everything worked out just fine: http://php.about.com/od/phpwithmysql/ss/Upload_file_sql.htm
不确定您遇到了什么错误,因为您似乎对这个过程非常清楚。我第一次这样做时,我使用本教程作为指导,一切都很好:http: //php.about.com/od/phpwithmysql/ss/Upload_file_sql.htm
Might I suggest storing the file in the database instead?? Less security issues that way!
我可以建议将文件存储在数据库中吗??这样安全问题就少了!
回答by Alfred
P.S: I got the feeling you are a beginner and I think I made a nice list at PHP Newbies: How to write good code
PS:我感觉你是个初学者,我想我在PHP Newbies: How to write good code上列了一个不错的清单
2) Save the images to a folder specified
2)将图像保存到指定的文件夹
move-uploaded-fileshould work. What comes to mind is that you have not set the right write permissions to move files. What kind of server are you using(Linux/Windows/MacOSX)? What does the function return as boolean(result)?
move-uploaded-file应该可以工作。我想到的是您没有设置正确的写入权限来移动文件。您使用的是哪种服务器(Linux/Windows/MacOSX)?函数作为布尔值(结果)返回什么?
One other thing: I need to have the image renamed to a random string (to prevent images being overwritten). I know I can do this with the rand() function, but I forgot to mention it in my original post.
另一件事:我需要将图像重命名为随机字符串(以防止图像被覆盖)。我知道我可以用 rand() 函数做到这一点,但我忘了在我原来的帖子中提到它。
rand()
is not really random and could give duplicate filenames. uniqidwill be unique
rand()
不是真正随机的,可能会给出重复的文件名。uniqid将是独一无二的
Edit: I figured out saving the descriptions to the database using $_SESSION['file_n_desc']. I just need to figure out how to upload the darn things and then save the locations into the database.
编辑:我想出使用 $_SESSION['file_n_desc'] 将描述保存到数据库中。我只需要弄清楚如何上传该死的东西,然后将位置保存到数据库中。
When using session when you have not overridden session you are not using your MySQL database but the file-system. You could read this tutorial to for store session in a database. Standard this session gets purged when closing the browser(just for that session). Just try this code:
在没有覆盖 session 的情况下使用 session 时,您不是在使用 MySQL 数据库,而是在使用文件系统。您可以阅读本教程以在数据库中存储会话。关闭浏览器时会清除此会话(仅针对该会话)。试试这个代码:
<?php
session_start();
echo "<p>";
if (isset($_SESSION['count'])) {
echo $_SESSION['count']++;
} else {
$_SESSION['count'] = 0;
echo $_SESSION['count']++;
}
echo "</p>";
echo "<p>" . session_id() . "</p>";
Load that page couple of times, without closing your browser. You will get something that looks like:
在不关闭浏览器的情况下加载该页面几次。你会得到类似的东西:
0
0
rlu10shi6l390il130qinlt913
rlu10shi6l390il130qinlt913
1
1
rlu10shi6l390il130qinlt913
rlu10shi6l390il130qinlt913
But when you close browser that session_id has changed and your counter has reset and you get output that looks like.
但是,当您关闭浏览器时,session_id 已更改并且您的计数器已重置,您会得到类似的输出。
0
fiakdijmmk38i40f39fm8u5mi4
1
fiakdijmmk38i40f39fm8u5mi4
Be able to call the images and descriptions in their classified listing in kind of a gallery setting
能够以画廊设置的形式调用其分类列表中的图像和描述
I used PDO(Good Read) to do (My)SQL. I did unit-testing(TDD) using phpunit. To access the database I used SQLite in memory mode(very nice to do SQL-testing) => new PDO('sqlite::memory:');
. I tried to follow Uncle Bob's three rules(Good Read).
我使用PDO(Good Read)来做(My)SQL。我使用phpunit进行了单元测试(TDD)。为了访问数据库,我在内存模式下使用了 SQLite(非常适合做 SQL 测试)=> new PDO('sqlite::memory:');
。我试图遵循鲍勃叔叔的三个规则(好读)。
Normally you split this in multiple files. Every class in a separate file. Every class should be tested in Isolation(loose coupling) in a separate file.
通常,您将其拆分为多个文件。每个类都在一个单独的文件中。每个类都应该在单独的文件中进行隔离(松散耦合)测试。
I used foreign keyto map "Listing to Image". I think this example is tested pretty thoroughly, but right now it is really time for me to get some sleep, so I am not sure ;).
我使用外键来映射“列表到图像”。我认为这个例子经过了非常彻底的测试,但现在我真的该睡觉了,所以我不确定;)。
<?php
function createDatabase() {
$db = new PDO('sqlite::memory:');
$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $db;
}
function createTables($db) {
// Listing table containing all listings.
$db->exec(
<<<EOT
CREATE TABLE IF NOT EXISTS listing(
id INTEGER PRIMARY KEY,
description TEXT NOT NULL UNIQUE)
EOT
);
// Image table containg all images.
$db->exec(
<<<EOT
CREATE TABLE IF NOT EXISTS image(
id INTEGER PRIMARY KEY,
listing_id INTEGER,
URL TEXT NOT NULL UNIQUE,
description TEXT NOT NULL UNIQUE,
FOREIGN KEY (listing_id) REFERENCES listing(id))
EOT
);
}
class Listing {
private $db;
private $id;
public $description;
/*private function __construct() {
}*/
private function __construct(PDO $db, $id, $description) {
$this->db = $db;
$this->id = $id;
$this->description = $description;
}
public static function create(PDO $db, $description) {
$stmt = $db->prepare(<<<EOT
INSERT OR IGNORE INTO listing(description)
VALUES (:description)
EOT
);
$stmt->execute(array(
":description" => $description
));
if ($stmt->rowCount() !== 1) {
return NULL;
}
return new Listing($db, $db->lastInsertId(), $description);
}
public static function get(PDO $db, $id) {
$stmt = $db->prepare("SELECT description FROM listing WHERE id = :id");
$stmt->execute(array(
":id" => $id
));
$row = $stmt->fetch();
if ($row == null) {
return null;
}
return new Listing($db, $id, $row['description']);
}
public function getImages() {
return Image::getImages($this->db, $this);
}
public function save() {
$stmt = $this->db->prepare(
<<<EOT
UPDATE listing SET description = :description WHERE id = :id
EOT
);
$stmt->execute(array(
":description" => $this->description,
":id" => $this->id
));
}
public function id() {
return $this->id;
}
}
class Image {
private $pdo;
public $URL;
private $id;
public $description;
private function __construct(PDO $pdo, $URL, $description, Listing $listing, $id) {
$this->pdo = $pdo;
$this->URL = $URL;
$this->description = $description;
$this->id = $id;
}
public static function create(PDO $pdo, $URL, $description, Listing $listing) {
$stmt = $pdo->prepare(
<<<EOT
INSERT OR IGNORE INTO image(URL, listing_id, description)
VALUES (:URL, :listing_id, :description)
EOT
);
$stmt->execute(array(
":URL" => $URL,
":listing_id" => $listing->id(),
":description" => $description
));
if ($stmt->rowCount() !== 1) {
return NULL;
}
return new Image($pdo, $URL, $description, $listing, $pdo->lastInsertId());
}
public function id() {
return $this->id;
}
public static function getImages(PDO $pdo, Listing $listing) {
$result = array();
$stmt = $pdo->prepare(
<<<EOT
SELECT * FROM image where listing_id = :listing_id
EOT
);
$stmt->execute(array(
":listing_id" => $listing->id(),
));
while($row = $stmt->fetch()) {
//$result[] = array($row['URL'], $row['description']);
$result[] = new Image($pdo, $row['URL'], $row['description'], $listing, $row['id']);
}
return $result;
}
}
class Test extends PHPUnit_Framework_TestCase {
protected $db;
protected function setUp() {
$this->db = createDatabase();
createTables($this->db);
}
public function testCreatingSingleListing() {
$listing1 = Listing::create($this->db, "Listing 1");
$this->assertEquals(1, $listing1->id());
}
public function testCreatingMultipleListings() {
$listing1 = Listing::create($this->db, "Listing 1");
$listing1 = Listing::create($this->db, "Listing 2");
$this->assertEquals(2, $listing1->id());
}
public function testReturningListingReturnsNullWhenNonexistence() {
$this->assertNull(Listing::get($this->db, 1));
}
public function testReturningCreatedListing() {
$Listing1 = Listing::create($this->db, "Listing 1");
$this->assertEquals("Listing 1", Listing::get($this->db, 1)->description);
}
public function testSavingListing() {
$listing1 = Listing::create($this->db, "Listing 1");
$listing1->description = "new";
$listing1->save();
$this->assertEquals("new", Listing::get($this->db, 1)->description);
}
public function testListingHasNoImagesWhenJustCreated() {
$listing1 = Listing::create($this->db, "Listing 1");
$this->assertEquals(array(), $listing1->getImages());
}
public function testAddingImageToListing() {
$listing1 = Listing::create($this->db, "Listing 1");
$image1 = Image::create($this->db, "http://localhost:12343/dfdfx/45.png", "first image", $listing1);
$this->assertEquals(array($image1), $listing1->getImages());
}
public function testAddingImagesToListing() {
$listing1 = Listing::create($this->db, "Listing 1");
$image1 = Image::create($this->db, "http://localhost:12343/dfdfx/45.png", "first image", $listing1);
$image2 = Image::create($this->db, "http://localhost:12343/df/46.png", "second image", $listing1);
$this->assertEquals(array($image1, $image2), $listing1->getImages());
}
}