• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

miaoxing / file / 13805620374

12 Mar 2025 06:59AM UTC coverage: 82.667%. Remained the same
13805620374

push

github

twinh
ci: upgrade actions/cache from v1 to v4

62 of 75 relevant lines covered (82.67%)

24.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.1
/src/Service/File.php
1
<?php
2

3
namespace Miaoxing\File\Service;
4

5
use Miaoxing\Plugin\BaseService;
6
use Wei\Ret;
7
use Wei\Upload;
8

9
/**
10
 * 处理文件相关逻辑
11
 *
12
 * 1. 将文件保存到 Storage 中,并记录到数据库中(save,saveRemote 等)
13
 * 2. 存储路径和扩展名管理,适用于本地存储(Localstorage),上传(Upload 服务)和下载操作(getAllowedExts,getRoot,generatePath 等)
14
 *
15
 * @mixin \LoggerMixin
16
 * @mixin \AppMixin
17
 * @mixin \ReqMixin
18
 * @mixin \FsMixin
19
 * @mixin \IsImageMixin
20
 * @mixin \HttpPropMixin
21
 * @mixin \StorageMixin
22
 * @mixin \LocalStorageMixin
23
 * @property Upload $upload (phpstan)不用 UploadMixin 以免和 upload 方法冲突
24
 */
25
class File extends BaseService
26
{
27
    /**
28
     * 将本地文件上传到文件存储中,按需原来文件,并记录文件到数据库
29
     *
30
     * @param string $path
31
     * @param array{origName?: string, size?: int, md5?: string, path?: string} $options
32
     * @return Ret
33
     * @svc
34
     */
35
    protected function saveLocal(string $path, array $options = []): Ret
36
    {
37
        // "public/" is meaningless for remote storage
38
        if (!isset($options['path'])) {
42✔
39
            $options['path'] = $this->fs->stripPublic($path);
42✔
40
        }
41

42
        $attributes = $this->getAttributes($path, $options);
42✔
43

44
        $ret = $this->storage->moveLocal($path, $options);
42✔
45
        if ($ret->isErr()) {
42✔
46
            $this->logger->warning('Failed to upload file', $ret->toArray() + ['path' => $path]);
×
47
            return $ret;
×
48
        }
49

50
        $file = $this->saveModel($attributes);
42✔
51

52
        return $file->toRet();
42✔
53
    }
54

55
    /**
56
     * 将远程文件上传到文件存储中,并记录到数据库
57
     *
58
     * @param string $url
59
     * @param array{path?: string, ext?: string} $options
60
     * @return Ret
61
     * @svc
62
     */
63
    protected function saveRemote(string $url, array $options = []): Ret
64
    {
65
        // 1. 获取远程文件
66
        $http = $this->http->request([
36✔
67
            'url' => $url,
36✔
68
            'timeout' => 10000,
36✔
69
            'referer' => true,
36✔
70
            'throwException' => false,
36✔
71
        ]);
36✔
72
        if (!$http->isSuccess()) {
36✔
73
            return $http->toRet();
×
74
        }
75

76
        // 2. 保存到本地
77
        // ignore query string
78
        $path = parse_url($url, \PHP_URL_PATH);
36✔
79
        if (!isset($options['ext'])) {
36✔
80
            $options['ext'] = $this->fs->getExt($path);
30✔
81
        }
82

83
        $writePath = $this->buildPath($options);
36✔
84
        $this->localStorage->write($writePath, $http->getResponse());
36✔
85

86
        // 3. 调用上传
87
        return $this->saveLocal($writePath, [
36✔
88
            'origName' => basename($path),
36✔
89
            'size' => strlen($http->getResponse()),
36✔
90
        ]);
36✔
91
    }
92

93
    /**
94
     * 获取用于存储文件的根目录
95
     *
96
     * @svc
97
     */
98
    protected function getRoot(): string
99
    {
100
        return $this->upload->getDir() . '/' . $this->app->getId();
42✔
101
    }
102

103
    /**
104
     * 生成用于存储文件的路径,包括目录和名称
105
     *
106
     * @svc
107
     */
108
    protected function generatePath(?string $ext = null): string
109
    {
110
        return $this->getRoot()
6✔
111
            . '/' . date('Ymd')
6✔
112
            . '/' . date('His') . mt_rand(100000, 999999)
6✔
113
            . ($ext ? '.' : '') . $ext;
6✔
114
    }
115

116
    /**
117
     * 根据扩展名称,获取文件类型
118
     */
119
    protected function detectType(?string $ext = null): int
120
    {
121
        if (null === $ext) {
42✔
122
            return FileModel::TYPE_OTHERS;
6✔
123
        }
124
        if ($this->upload->isAllowedImageExt($ext)) {
36✔
125
            return FileModel::TYPE_IMAGE;
30✔
126
        }
127
        return FileModel::TYPE_OTHERS;
6✔
128
    }
129

130
    /**
131
     * 根据传入的选项生成路径
132
     *
133
     * @param array{path?: string, ext?: string} $options
134
     * @return string
135
     * @internal
136
     */
137
    protected function buildPath(array $options = []): string
138
    {
139
        $path = $this->getRoot();
36✔
140

141
        // 指定了完整的路径,例如远程文件同步到本地
142
        if (isset($options['path'])) {
36✔
143
            return $path . '/' . $options['path'];
6✔
144
        }
145

146
        // 生成默认路径
147
        return $this->generatePath($options['ext'] ?? null);
30✔
148
    }
149

150
    /**
151
     * 根据文件路径获取文件的属性
152
     *
153
     * @internal
154
     */
155
    protected function getAttributes(string $path, array $options = []): array
156
    {
157
        $ext = $this->fs->getExt($path);
42✔
158

159
        $attributes = [
42✔
160
            'path' => $path,
42✔
161
            'origName' => $this->truncate($options['origName'] ?? null, 64),
42✔
162
            'size' => $options['size'] ?? filesize($path),
42✔
163
            'md5' => $options['md5'] ?? md5_file($path),
42✔
164
            'ext' => $ext,
42✔
165
            'type' => $this->detectType($ext),
42✔
166
            'url' => $this->storage->getUrl($options['path'] ?? $path),
42✔
167
        ];
42✔
168

169
        // 计算图片宽高
170
        if ($this->isImage->isValid($path)) {
42✔
171
            $attributes['width'] = $this->isImage->getOption('width');
6✔
172
            $attributes['height'] = $this->isImage->getOption('height');
6✔
173
        }
174

175
        return $attributes;
42✔
176
    }
177

178
    /**
179
     * 保存文件记录到数据库中
180
     *
181
     * @param array $attributes
182
     * @return FileModel
183
     * @internal
184
     */
185
    protected function saveModel(array $attributes): FileModel
186
    {
187
        return FileModel::saveAttributes($attributes);
6✔
188
    }
189

190
    /**
191
     * @internal
192
     */
193
    protected function truncate(?string $str, int $length): ?string
194
    {
195
        if (mb_strlen($str) > $length) {
42✔
196
            return mb_substr($str, 0, $length);
×
197
        }
198
        return $str;
42✔
199
    }
200
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc