我必须为我的组织创建一个照片在线商店,并提供一些其他功能。但主要的想法是允许用户上传他们的数码照片(仅限JPEG格式)并在商店中出售。这个项目与iStockPhotos,Fotolia等网站有很大关系......
对于JPEG图像,我应该遵循哪些标准,例如最小和最大尺寸和质量?
我可以使用像Imagine这样的库来制作原始的水印和缩略图。但我在这里主要关注的是如何安全地将原始文件和重复文件安全地存储在适当的文件夹结构中,如何在有人购买时创建原始文件的下载链接?
我打算使用PHP Yii2框架从头开始构建它。所以请给我任何指导方针,使其成功。提前谢谢。
答案 0 :(得分:1)
我将我的答案分成三部分。上传,保存和下载过程。我希望你有使用PHP和Yii2的经验,所以我不会对整个事情进行编码,但给你的要点。
这个很简单。只需创建一个表单,您可以上传图像文件。确保设置正确的enctype(multipart/form-data
)。使用Yii2进行文件上传的基础知识已解释为here。
保存文件时,您需要执行两个步骤:
对于第二项任务,我可以向您推荐我使用的yurkinx/yii2-image库。它内置了调整大小和水印。
我通常将它们放在@runtime
内的专用文件夹中。运行时文件夹位于项目根目录中,因此无法从Web访问。您的文件夹可以具有以下路线,您可以将其放在params.php
中以供日后参考。
@runtime/shop_images/
对于实际的文件名,您可以使用我接下来将解释的模型的ID。您的文件名可以是:
你应该有照片模型。每个上传的照片将由此模型db-table中的一行表示。您还需要一个表来连接用户及其购买的图像(m:n)。如果您需要确切的架构,请告诉我。
注意......您需要先创建模型实例并保存。否则你不会有id来命名实际的文件!最简单的方法是覆盖照片模型的save()
- 方法,因为beforeSave()
太早(还没有id!)而afterSave()
太迟了(不再有回滚的可能性)。
这个比听起来容易。您可以在照片控制器中创建一个操作,让用户下载图片。
在操作中,您首先检查用户是否已购买图片。您可以轻松完成此操作,因为您只需要检查user_photo
- 表中是否有一行将当前登录的用户链接到所请求的照片。
如果有连接,您就可以输出照片了。这包括两个步骤:
您可以完全手动或use my extension完成此操作。这是手动方式:
public function actionPicture($id) {
//find the model (will throw excetion if not)
$model = $this->findModel($id);
//check if user is allowed
if (!UserPhoto::find()->user(Yii::$app->user->id)->photo($model)->exists()) {
//throw not allowed exception here!
}
//get the path to your photo
$photoPath= Yii::getAlias('@runtime/shop_images') . DIRECTORY_SEPARATOR . $model->id . '.jpg');
$photoPath= FileHelper::normalizaPath($imagePath);
//prepare the response
$response = Yii::$app->response;
$response->headers->set('Content-type', 'image/jpg');
$response->headers->set('Content-length', filesize($photoPath));
$response->format = \yii\web\Response::FORMAT_RAW;
//return the contents
return file_get_contents($photoPath);
}
为了让您对此有所了解。无论您从某个操作返回什么内容,都会将其放入您的回复的data
- 属性中。然后将其格式化为响应的content
属性。这将如何发生取决于您设置的响应格式。在我们的例子中,我们选择了FORMAT_RAW
,它将数据保留原样。现在我们可以简单地返回照片文件的内容,我们就完成了。
您可以通过指定其他标头来进一步指定文件的处理方式。这样,您可以强制下载某个文件名:
$headers->set('Content-disposition', 'attachment; filename="' . $myCustomFileName . '"');
与为拇指创建动作的方法相同,或者使用第二个参数扩展此动作,该参数告诉您要返回的图像大小。
我希望我能提供帮助。如果您需要有关其中一个步骤的更多信息,请不要犹豫!
以下是创建所需的两个表的可能迁移。省略了用户表,因为您可能已经创建了这个用户表。
public function up() {
//the photo table and the relation to the owning user
$this->createTable('{{%photo}}', [
'id'=>$this->primaryKey(),
'owner_user_id'=>$this->integer(),
'original_filename'=>$this->string()->notNull(),
'filesize'=>$this->integer()->notNull(),
'extension'=>$this->string(4)->notNull(),
'meta_data'=>$this->text(),
]);
$this->addForeignKey('FK_photo_user', '{{%photo}}', 'owner_user_id', '{{%user}}', 'id', 'SET NULL', 'CASCADE');
//the m:n-table mapping users to their purchased photos
$this->createTable('{{%user_photo}}', [
'user_id'=>$this->integer()->notNull(),
'photo_id'=>$this->integer()->notNull(),
'PRIMARY KEY (user_id, photo_id)',
]);
$this->addForeignKey('FK_user_photo_user', '{{%user_photo}}', 'user_id', '{{%user}}', 'id', 'CASCADE', 'CASCADE');
$this->addForeignKey('FK_user_photo_photo', '{{%user_photo}}', 'photo_id', '{{%photo}}', 'id', 'CASCADE', 'CASCADE');
return true;
}
如果您查看架构,您将获得要点。
如果您希望保存照片的元数据,通常是EXIF数据,您当然也可以这样做。我不得不警告你......实际上没有真正的标准,相机制造商存储他们的价值观,因为我目前在我们的一个项目中体验。在我们的例子中,最重要的信息是"日期和#34; -field。根据制造商的不同,这可能在几个地方。更复杂的是EXIF中的数据类型。我们在那里遇到非法字符和你可以想象的每个字符编码都有很大的麻烦。
尽管如此,我还会告诉您如何在文本列中以JSON格式保存数据。最大的优点是你拥有原有的所有结构。我还在上面的迁移中添加了该列。
$exif = exif_read_data($model->localFilePath, 'FILE,EXIF');
if ($exif !== false) {
$model->meta_Data = Json::encode($exif);
}
请务必阅读关于exif_read_data()
的{{3}},因为它确实存在问题!