图片上传存储策略

     2023-02-19     26

关键词:

【中文标题】图片上传存储策略【英文标题】:Image upload storage strategies 【发布时间】:2011-02-08 13:43:14 【问题描述】:

当用户上传图片到我的网站时,图片会经过这个过程;

用户上传图片 将图片元数据存储在数据库中,为图片赋予唯一的ID 异步图像处理(缩略图创建、裁剪等) 所有图片都存储在同一个上传文件夹中

到目前为止,该网站还很小,上传目录中只有大约 200,000 张图片。我意识到我远未达到目录中文件的物理限制,但这种方法显然无法扩展,所以我想知道是否有人对处理大量图像上传的上传/存储策略有任何建议。

编辑: 创建用户名(或更具体地说,用户 ID)子文件夹似乎是一个很好的解决方案。通过更多的挖掘,我在这里找到了一些很棒的信息; How to store images in your filesystem 但是,如果购买 CDN,这种 userid dir 方法是否可以很好地扩展?

【问题讨论】:

您是否考虑过为每个用户创建一个文件夹,可能使用 /letter/username 格式(例如 images/o/omg_unicornsimages/p/powerlord 可行,但用户名可以更改。我将编辑并添加此信息。 【参考方案1】:

MediaWiki 生成上传文件名称的 MD5 和,并使用 MD5 的前两个字母(例如,和“cf1e66b779​​18167a6b6b972c12b1c00d”的“c”和“f”)创建此目录结构:

images/c/cf/Whatever_filename.png

您还可以使用图像 ID 来确定每个目录的文件数上限。也许用floor(image unique ID / 1000) 来确定父目录,每个目录有 1000 张图像。

【讨论】:

我们使用类似的方法,但采用 4 级深度结构:12/34/56/78 适用于数百万个文件。 图片ID是什么?如何在 PHP 中找到这个? +用户随意为自行车涂上你喜欢的任何颜色。【参考方案2】:

您可以考虑使用开源 http://danga.com/mogilefs/,因为它非常适合您的工作。它将带您从考虑文件夹到名称空间(可能是用户)并让它为您存储图像。最好的部分是您不必关心数据的存储方式。它使其完全冗余,您甚至可以设置关于冗余缩略图的控制。

【讨论】:

【参考方案3】:

您是否考虑过使用 Amazon S3 之类的东西来存储文件?我经营一家照片托管公司,在我们自己的服务器上很快达到限制后,我们切换到了 AmazonS3。 S3 的美妙之处在于没有 inode 之类的限制,您只需不断向其扔文件即可。

另外:如果您不喜欢 S3,您可以随时尝试将其分解为尽可能多的子文​​件夹:

/userid/年/月/日/photoid.jpg

【讨论】:

【参考方案4】:

我之前回答过一个类似的问题,但我找不到它,也许 OP 删除了他的问题......

无论如何,Adams solution 似乎是迄今为止最好的,但它不是防弹的,因为images/c/cf/(或任何其他目录/子目录对)仍可包含多达 16^30 个唯一哈希 如果我们计算图像扩展名,文件数量至少要多 3 倍,远远超过任何常规文件系统可以处理的数量。

AFAIK,SourceForge.net 也将此系统用于项目存储库,例如 "fatfree" project 将放置在 projects/f/fa/fatfree/,但我相信他们将项目名称限制为 8 个字符。


我会在数据库中存储图像哈希以及 DATE / DATETIME / TIMESTAMP 字段,指示图像何时上传/处理,然后将图像放置在如下结构中:

images/
  2010/                                      - Year
    04/                                      - Month
      19/                                    - Day
        231c2ee287d639adda1cdb44c189ae93.png - Image Hash

或者:

images/
  2010/                                    - Year
    0419/                                  - Month & Day (12 * 31 = 372)
      231c2ee287d639adda1cdb44c189ae93.png - Image Hash

除了更具描述性之外,这种结构足以托管数十万张(取决于您的文件系统限制)几千年来每天的图像,这是就像 Wordpress 和其他人做的那样,我认为他们在这个上做对了。

可以在数据库中轻松查询重复的图像,您只需创建符号链接。

当然,如果这对您来说还不够,您可以随时添加更多子目录(小时、分钟、...)。

我个人不会使用用户 ID,除非您的数据库中没有该信息,因为:

    在 URL 中披露用户名 用户名不稳定(您也许可以重命名文件夹,但仍然...) 假设用户可以上传大量图片 毫无用处 (?)

关于 CDN,我看不出这个方案(或任何其他方案)不起作用的任何原因......

【讨论】:

【参考方案5】:

您可以将用户名转换为 md5,并将 md5 转换后的用户名的 2-3 个首字母设置为一个文件夹,用于头像和图像,您可以使用时间、随机字符串、ID 和名称进行转换和播放

8648b8f3ce06a7cc57cf6fb931c91c55 - devcline

也是下一个文件夹或反向文件夹的用户名或 ID 的第一个字母

看起来像

结构:

stream/img/86/8b8f3ce06a7cc57cf6fb931c91c55.png    //simplest
stream/img/d/2/0bbb630d63262dd66d2fdde8661a410075.png //first letter and id folders
stream/img/864/d/8b8f3ce06a7cc57cf6fb931c91c55.png // with first letter of the nick
stream/img/864/2/8b8f3ce06a7cc57cf6fb931c91c55.png   //with unique id
stream/img/2864/8b8f3ce06a7cc57cf6fb931c91c55.png    //with unique id in 3 letters
stream/img/864/2_8b8f3ce06a7cc57cf6fb931c91c55.png   //with unique id in picture name

代码

$username = substr($username_md5, 1); // to cut first letter from the md5 converted nick
$username_first = $username[0]; // the first letter
$username_md5 = md5($username); // md5 for username
$randomname = uniqid($userid).md5(time());  //for generate a random name based on ID

你也可以试试 base64

 $image_encode = strtr(base64_encode($imagename), '+/=', '-_,');
 $image_decode = base64_decode(strtr($imagename, '-_,', '+/='));

Steam 和 dokuwiki 使用这种结构。

【讨论】:

【参考方案6】:

是的,是的,我知道这是一个古老的话题。但是存储大量图像以及如何组织底层文件夹结构的问题。所以我提出了我的处理方法,希望这可以帮助一些人。

使用 md5 哈希的想法是处理海量图像存储的最佳方式。请记住,不同的值可能具有相同的哈希值,我强烈建议将用户 ID 或 nicname 也添加到路径中以使其唯一。是的,这就是我们所需要的。如果某人有不同的用户具有相同的数据库 ID - 那么,有问题;)所以root_path/md5_hash/user_id 是你需要做的一切。

顺便说一句,使用 DATE / DATETIME / TIMESTAMP 并不是最佳解决方案。在忙碌的一天,你会得到一大堆图像文件夹,而在不常去的时候,这些文件夹几乎是空的。不确定这会导致性能问题,但存在数据美学之类的问题,并且一致的数据分布始终是优越的。

所以我显然选择了哈希解决方案。

我编写了以下函数来轻松生成此类基于散列的存储路径。如果您喜欢,请随意使用它。

/**
* Generates directory path using $user_id md5 hash for massive image storing 
* @author Hexodus 
* @param string $user_id numeric user id
* @param string $user_root_raw root directory string
* @return null|string
*/

function getUserImagePath($user_id = null, $user_root_raw = "images/users", $padding_length = 16, 
                            $split_length = 3, $hash_length = 12, $hide_leftover = true)

    // our db user_id should be nummeric
    if (!is_numeric($user_id))
        return null;

    // clean trailing slashes  
    $user_root_rtrim = rtrim( $user_root_raw, '/\\' );
    $user_root_ltrim = ltrim( $user_root_rtrim, '/\\' );
    $user_root = $user_root_ltrim;

    $user_id_padded = str_pad($user_id, $padding_length, "0", STR_PAD_LEFT); //pad it with zeros  
    $user_hash = md5($user_id); // build md5 hash

    $user_hash_partial = $hash_length >=1 && $hash_length < 32 
                        ? substr($user_hash, 0, $hash_length) : $user_hash;
    $user_hash_leftover = $user_hash_partial <= 32 ? substr($user_hash, $hash_length, 32) : null;

    $user_hash_splitted = str_split($user_hash_partial, $split_length); //split in chunks
    $user_hash_imploded = implode($user_hash_splitted,"/"); //glue aray chunks with slashes

    if ($hide_leftover || !$user_hash_leftover)
        $user_image_path = "$user_root/$user_hash_imploded/$user_id_padded"; //build final path
    else
        $user_image_path = "$user_root/$user_hash_imploded/$user_hash_leftover/$user_id_padded"; //build final path plus leftover

    return $user_image_path;

功能测试调用:

$user_id = "1394";
$user_root = "images/users"; 
$user_hash = md5($user_id);
$path_sample_basic = getUserImagePath($user_id);
$path_sample_advanced = getUserImagePath($user_id, "images/users", 8, 4, 12, false);

echo "<pre>hash: $user_hash</pre>";
echo "<pre>basic:<br>$path_sample_basic</pre>";
echo "<pre>customized:<br>$path_sample_advanced</pre>";
echo "<br><br>";

生成的输出 - 为了您的方便而着色;):

【讨论】:

很好的答案.. 绝对帮助我最好地理解散列存储。虽然你的分区散列在 users/ 之后不是有点长吗?如果它是 4 十六进制长(如 f016),这是否意味着可能有 15*15*15*15 (50625) 个文件夹可以存储在那里?如果它是 2 十六进制长 (f0),最大文件夹将是 15*15 (256)?这不是更可取吗?在您的原始图像中,您将 md5 哈希划分为 8 个长度为 4 十六进制的不同目录。这不是太过分了,浏览这么多子文件夹会影响性能吗? @user3614030 很高兴我的回答对您有所帮助。如您所见,我还使用了通常是数据库中唯一 ID 的 ID,因此不需要完整长度的哈希。老实说,我不知道子文件夹是否会影响性能。【参考方案7】:

我已经使用了很长时间的灵魂。这是相当老的代码,可以进一步优化,但它仍然可以很好地发挥作用。

这是一个不可变的函数创建目录结构基于:

    标识图像的编号(文件 ID):

建议此数字对于基目录是唯一的,如数据库表的主键,但不是必需的。

    基本目录

    文件和第一级子目录的最大期望数量。这个承诺只有在每个 FILE ID 都是唯一的情况下才能兑现。

使用示例:

使用显式文件 ID:

$fileName = 'my_image_05464hdfgf.jpg';
$fileId = 65347;
$baseDir = '/home/my_site/www/images/';
$baseURL = 'http://my_site.com/images/';

$clusteredDir = \DirCluster::getClusterDir( $fileId );
$targetDir = $baseDir . $clusteredDir;
$targetPath = $targetDir . $fileName;
$targetURL = $baseURL . $clusteredDir  . $fileName;

使用文件名,数字= crc32(文件名)

$fileName = 'my_image_05464hdfgf.jpg';
$baseDir = '/home/my_site/www/images/';
$baseURL = 'http://my_site.com/images/';

$clusteredDir = \DirCluster::getClusterDir( $fileName );
$targetDir = $baseDir . $clusteredDir;
$targetURL = $baseURL . $clusteredDir  . $fileName;

代码:

class DirCluster 


/**
* @param mixed $fileId       - numeric FILE ID or file name
* @param int $maxFiles       - max files in one dir
* @param int $maxDirs        - max 1st lvl subdirs in one dir
* @param boolean $createDirs - create dirs?
* @param string $path        - base path used when creatign dirs
* @return boolean|string
*/
public static function getClusterDir($fileId, $maxFiles = 100, $maxDirs = 10,
$createDirs = false, $path = "") 

// Value for return
$rt = '';

// If $fileId is not numerci - lets create crc32
if (!is_numeric($fileId)) 
    $fileId = crc32($fileId);


if ($fileId < 0) 
  $fileId = abs($fileId);


if ($createDirs) 

    if (!file_exists($path))
    
        // Check out the rights - 0775 may be not the best for you
        if (!mkdir($path, 0775))  
          return false;
        
        @chmod($path, 0775);
    


if ( $fileId <= 0 || $fileId <= $maxFiles )  
  return $rt;


// Rest from dividing
$restId = $fileId%$maxFiles;

$formattedFileId = $fileId - $restId;

// How many directories is needed to place file
$howMuchDirs = $formattedFileId / $maxFiles;

while ($howMuchDirs > $maxDirs)

    $r = $howMuchDirs%$maxDirs;
    $howMuchDirs -= $r;
    $howMuchDirs = $howMuchDirs/$maxDirs;
    $rt .= $r . '/'; // DIRECTORY_SEPARATOR = /

    if ($createDirs)
    
        $prt = $path.$rt;
        if (!file_exists($prt))
        
            mkdir($prt);
            @chmod($prt, 0775);
        
    


$rt .= $howMuchDirs-1;
if ($createDirs)

    $prt = $path.$rt;
    if (!file_exists($prt))
    
        mkdir($prt);
        @chmod($prt, 0775);
    


$rt .= '/'; // DIRECTORY_SEPARATOR

return $rt;





【讨论】:

令人困惑的 s3 存储桶策略上传问题

】令人困惑的s3存储桶策略上传问题【英文标题】:confusings3bucketpolicyuploadissue【发布时间】:2021-06-0423:59:44【问题描述】:我希望我的应用能够将对象放入s3存储桶中,并且我希望公众能够读取这些对象。出于某种原因,除非我... 查看详情

如何使用 AWS S3 存储桶策略强制执行文件类型上传

】如何使用AWSS3存储桶策略强制执行文件类型上传【英文标题】:HowcanienforcefiletypeuploadswithanAWSS3bucketpolicy【发布时间】:2013-06-1117:37:25【问题描述】:使用AWSS3的存储桶策略,是否可以强制上传的文件(PutObject)具有文件扩展名“.t... 查看详情

在 Firebase 存储中上传图片

】在Firebase存储中上传图片【英文标题】:UploadingpictureinFirebasestorageflutterdart【发布时间】:2021-12-2303:11:07【问题描述】:我正在尝试从我的画廊上传图片,代码运行良好并且正在运行,但不在我的设备上。我想知道为什么它对... 查看详情

将 Facebook 图片 URL 上传到 Firebase 存储

】将Facebook图片URL上传到Firebase存储【英文标题】:UploadFacebookimageURLtoFirebaseStorage【发布时间】:2016-12-2417:52:44【问题描述】:我正在尝试将用户的Facebook个人资料图片上传到Firebase存储。letdictionary=resultas?NSDictionaryletdata=dictionary?.... 查看详情

将图片从 URL 上传到 Firebase 存储

】将图片从URL上传到Firebase存储【英文标题】:UploadimagefromURLtoFirebaseStorage【发布时间】:2016-10-0718:14:54【问题描述】:我想知道如何通过URL而不是输入(例如)将文件上传到Firebase的存储中。我正在从网站上抓取图像并检索它们... 查看详情

将图片从 URL 上传到 Firebase 存储

】将图片从URL上传到Firebase存储【英文标题】:UploadimagefromURLtoFirebaseStorage【发布时间】:2020-04-0101:29:03【问题描述】:我想知道如何通过URL而不是输入(例如)将文件上传到Firebase的存储中。我正在从网站上抓取图像并检索它们... 查看详情

代号一上传图片到S3存储桶权限

】代号一上传图片到S3存储桶权限【英文标题】:CodenameoneuploadimagetoS3bucketpermission【发布时间】:2017-05-0511:14:26【问题描述】:代号一的Shai帮助我将图像上传到s3存储桶,我能够上传图像,我碰巧注意到,当我从浏览器访问图像... 查看详情

无法将 facebook 个人资料图片 (Uri) 上传到 Firebase 存储

】无法将facebook个人资料图片(Uri)上传到Firebase存储【英文标题】:Unabletouploadfacebookprofilepicture(Uri)toFirebaseStorage【发布时间】:2017-02-2308:07:52【问题描述】:我正在尝试从facebook获取个人资料图片,然后将其存储在firebase存储中。... 查看详情

将图片上传到 Firebase 存储并显示为个人资料图片

】将图片上传到Firebase存储并显示为个人资料图片【英文标题】:UploadimagetoFirebaseStorageandshowasProfileImage【发布时间】:2019-10-1718:12:36【问题描述】:我在将图片上传到Firebase存储方面需要帮助。我的应用程序中有一个配置文件菜... 查看详情

上传图片并存储到数据库

】上传图片并存储到数据库【英文标题】:Uploadimageandstoretodatabase【发布时间】:2017-11-0503:19:08【问题描述】:我又要重新开始编程了,但由于无法将图像上传到我的数据库,我目前陷入困境。昨晚我正在阅读一些代码和答案,... 查看详情

图片上传存储获得外链方法

...用markdown写一些博客或者文章的时候,常常需要引用一些图片,一般都是找一个免费的图床上传,然后复制图片链接在我们的markdown文章中。类似像这样:存在的隐患一般的免费图片托管网站有很大的可能在某一天停止运营,或者... 查看详情

图片的批量导入实现和对主键生成策略的思考

...了问题都会影响上线的时间。现在一期上了线,也对自己图片上传导入这部分做一些总结,还有对现有方法不足之处的一些思考。其实一张图的上传逻辑还是比较简单的:1.上传图片到 查看详情

ref 不是函数,firebase 存储图片上传,reactJS

】ref不是函数,firebase存储图片上传,reactJS【英文标题】:refisnotafunction,firebasestorageimageupload,reactJS【发布时间】:2020-12-1113:55:27【问题描述】:大家好,我在尝试将图像上传到我的Firebase存储时不断收到此错误。未处理的拒绝(... 查看详情

vue.js+云存储(实现图片上传功能)(代码片段)

利用vue.js+element-ui实现云存储上传图片功能文章目录前言一、对象存储二、配置腾讯云Cos1.引入库第一步拥有腾讯云的开发者账号实名认证三、新建文件上传组件新建上传图片组件src/components/ImageUpload/index.vuedetail详情页引入构... 查看详情

ueditor上传图片到七牛云存储(formapi,java)

转:http://my.oschina.net/duoduo3369/blog/174655ueditor上传图片到七牛云存储ueditor结合七牛传图片七牛的试炼开发前的准备与注意事项说明Let‘srock与ueditor结合前的准备工作首先从表单开始生成token建立图片空间生成token上传流程集成ueditor... 查看详情

自动为整个 S3 存储桶设置缓存控制(使用存储桶策略?)

...略中执行此操作。我知道我可以编辑现有的,如果我自己上传它们,我知道如何在put上指定它们,但 查看详情

从专用用户访问 S3 存储桶(策略失败?)

...创建一个具有专用用户的S3存储桶,以便使用terraform进行上传/下载。由于某种原因,正在创建的用户无法访问存储桶:$awsiamlist-attached-user-policies--user-name 查看详情

用fileupload控件上传图片后,如何将读取出的图片路径存储下来?

参考技术AFileUpLoad有一个属性叫File什么的那个就有图片的路径详情咨询384474000 查看详情