summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2018-08-16 07:59:16 +0900
committerGitHub <noreply@github.com>2018-08-16 07:59:16 +0900
commita8fb0d477fb67661251c57910a0c4f3d2ac856d1 (patch)
tree5986efd36e0a42acd290a37403059eb64a63b13e /src
parentMerge pull request #2252 from syuilo/greenkeeper/minio-7.0.0 (diff)
parent:v: (diff)
downloadmisskey-a8fb0d477fb67661251c57910a0c4f3d2ac856d1.tar.gz
misskey-a8fb0d477fb67661251c57910a0c4f3d2ac856d1.tar.bz2
misskey-a8fb0d477fb67661251c57910a0c4f3d2ac856d1.zip
Merge pull request #2251 from syuilo/provide-thumbnails
Provide drive file thumbnails
Diffstat (limited to 'src')
-rw-r--r--src/client/app/desktop/views/components/drive.file.vue2
-rw-r--r--src/client/app/desktop/views/components/media-image.vue2
-rw-r--r--src/client/app/mobile/views/components/drive.file.vue2
-rw-r--r--src/client/app/mobile/views/components/media-image.vue2
-rw-r--r--src/models/drive-file.ts2
-rw-r--r--src/server/file/send-drive-file.ts27
-rw-r--r--src/services/drive/add-file.ts61
-rw-r--r--src/services/drive/delete-file.ts6
8 files changed, 74 insertions, 30 deletions
diff --git a/src/client/app/desktop/views/components/drive.file.vue b/src/client/app/desktop/views/components/drive.file.vue
index 55218625c1..3b5be19dcf 100644
--- a/src/client/app/desktop/views/components/drive.file.vue
+++ b/src/client/app/desktop/views/components/drive.file.vue
@@ -16,7 +16,7 @@
<p>%i18n:@banner%</p>
</div>
<div class="thumbnail" ref="thumbnail" :style="`background-color: ${ background }`">
- <img :src="file.url" alt="" @load="onThumbnailLoaded"/>
+ <img :src="file.thumbnailUrl" alt="" @load="onThumbnailLoaded"/>
</div>
<p class="name">
<span>{{ file.name.lastIndexOf('.') != -1 ? file.name.substr(0, file.name.lastIndexOf('.')) : file.name }}</span>
diff --git a/src/client/app/desktop/views/components/media-image.vue b/src/client/app/desktop/views/components/media-image.vue
index 74bb03f4ed..8b68f260fa 100644
--- a/src/client/app/desktop/views/components/media-image.vue
+++ b/src/client/app/desktop/views/components/media-image.vue
@@ -37,7 +37,7 @@ export default Vue.extend({
style(): any {
return {
'background-color': this.image.properties.avgColor && this.image.properties.avgColor.length == 3 ? `rgb(${this.image.properties.avgColor.join(',')})` : 'transparent',
- 'background-image': this.raw ? `url(${this.image.url})` : `url(${this.image.url})`
+ 'background-image': this.raw ? `url(${this.image.url})` : `url(${this.image.thumbnailUrl})`
};
}
},
diff --git a/src/client/app/mobile/views/components/drive.file.vue b/src/client/app/mobile/views/components/drive.file.vue
index 776e11ecf8..c337629cb6 100644
--- a/src/client/app/mobile/views/components/drive.file.vue
+++ b/src/client/app/mobile/views/components/drive.file.vue
@@ -43,7 +43,7 @@ export default Vue.extend({
thumbnail(): any {
return {
'background-color': this.file.properties.avgColor && this.file.properties.avgColor.length == 3 ? `rgb(${this.file.properties.avgColor.join(',')})` : 'transparent',
- 'background-image': `url(${this.file.url})`
+ 'background-image': `url(${this.file.thumbnailUrl})`
};
}
},
diff --git a/src/client/app/mobile/views/components/media-image.vue b/src/client/app/mobile/views/components/media-image.vue
index d9d68fa7ba..e40069bbe3 100644
--- a/src/client/app/mobile/views/components/media-image.vue
+++ b/src/client/app/mobile/views/components/media-image.vue
@@ -27,7 +27,7 @@ export default Vue.extend({
},
computed: {
style(): any {
- let url = `url(${this.image.url})`;
+ let url = `url(${this.image.thumbnailUrl})`;
if (this.$store.state.device.loadRemoteMedia || this.$store.state.device.lightmode) {
url = null;
diff --git a/src/models/drive-file.ts b/src/models/drive-file.ts
index 38e1bf549c..358dd89441 100644
--- a/src/models/drive-file.ts
+++ b/src/models/drive-file.ts
@@ -31,6 +31,7 @@ export type IMetadata = {
comment: string;
uri?: string;
url?: string;
+ thumbnailUrl?: string;
src?: string;
deletedAt?: Date;
withoutChunks?: boolean;
@@ -164,6 +165,7 @@ export const pack = (
_target = Object.assign(_target, _file.metadata);
_target.url = _file.metadata.url ? _file.metadata.url : `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}`;
+ _target.thumbnailUrl = _file.metadata.thumbnailUrl ? _file.metadata.thumbnailUrl : `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}?thumbnail`;
_target.isRemote = _file.metadata.isRemote;
if (_target.properties == null) _target.properties = {};
diff --git a/src/server/file/send-drive-file.ts b/src/server/file/send-drive-file.ts
index 1a76b0e41f..b904bda91b 100644
--- a/src/server/file/send-drive-file.ts
+++ b/src/server/file/send-drive-file.ts
@@ -1,5 +1,3 @@
-import * as fs from 'fs';
-
import * as Koa from 'koa';
import * as send from 'koa-send';
import * as mongodb from 'mongodb';
@@ -51,23 +49,16 @@ export default async function(ctx: Koa.Context) {
};
if ('thumbnail' in ctx.query) {
- // 画像以外
- if (!file.contentType.startsWith('image/')) {
- const readable = fs.createReadStream(`${__dirname}/assets/thumbnail-not-available.png`);
- ctx.set('Content-Type', 'image/png');
- ctx.body = readable;
- } else if (file.contentType == 'image/gif') {
- // GIF
- await sendRaw();
+ const thumb = await DriveFileThumbnail.findOne({
+ 'metadata.originalId': fileId
+ });
+
+ if (thumb != null) {
+ ctx.set('Content-Type', 'image/jpeg');
+ const bucket = await getDriveFileThumbnailBucket();
+ ctx.body = bucket.openDownloadStream(thumb._id);
} else {
- const thumb = await DriveFileThumbnail.findOne({ 'metadata.originalId': fileId });
- if (thumb != null) {
- ctx.set('Content-Type', 'image/jpeg');
- const bucket = await getDriveFileThumbnailBucket();
- ctx.body = bucket.openDownloadStream(thumb._id);
- } else {
- await sendRaw();
- }
+ await sendRaw();
}
} else {
if ('download' in ctx.query) {
diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts
index 701d547776..b8a2a33da4 100644
--- a/src/services/drive/add-file.ts
+++ b/src/services/drive/add-file.ts
@@ -1,6 +1,5 @@
import { Buffer } from 'buffer';
import * as fs from 'fs';
-import * as stream from 'stream';
import * as mongodb from 'mongodb';
import * as crypto from 'crypto';
@@ -17,30 +16,52 @@ import { publishUserStream, publishDriveStream } from '../../stream';
import { isLocalUser, IUser, IRemoteUser } from '../../models/user';
import delFile from './delete-file';
import config from '../../config';
+import { getDriveFileThumbnailBucket } from '../../models/drive-file-thumbnail';
const log = debug('misskey:drive:add-file');
-async function save(readable: stream.Readable, name: string, type: string, hash: string, size: number, metadata: any): Promise<IDriveFile> {
+async function save(path: string, name: string, type: string, hash: string, size: number, metadata: any): Promise<IDriveFile> {
+ let thumbnail: Buffer;
+
+ if (['image/jpeg', 'image/png', 'image/webp'].includes(type)) {
+ thumbnail = await sharp(path)
+ .resize(300)
+ .jpeg({
+ quality: 50,
+ progressive: true
+ })
+ .toBuffer();
+ }
+
if (config.drive && config.drive.storage == 'minio') {
const minio = new Minio.Client(config.drive.config);
const id = uuid.v4();
const obj = `${config.drive.prefix}/${id}`;
+ const thumbnailObj = `${obj}-thumbnail`;
const baseUrl = config.drive.baseUrl
|| `${ config.drive.config.secure ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? ':' + config.drive.config.port : '' }/${ config.drive.bucket }`;
- await minio.putObject(config.drive.bucket, obj, readable, size, {
+ await minio.putObject(config.drive.bucket, obj, fs.createReadStream(path), size, {
'Content-Type': type,
'Cache-Control': 'max-age=31536000, immutable'
});
+ if (thumbnail) {
+ await minio.putObject(config.drive.bucket, thumbnailObj, fs.createReadStream(path), size, {
+ 'Content-Type': 'image/jpeg',
+ 'Cache-Control': 'max-age=31536000, immutable'
+ });
+ }
+
Object.assign(metadata, {
withoutChunks: true,
storage: 'minio',
storageProps: {
id: id
},
- url: `${ baseUrl }/${ obj }`
+ url: `${ baseUrl }/${ obj }`,
+ thumbnailUrl: thumbnail ? `${ baseUrl }/${ thumbnailObj }` : null
});
const file = await DriveFile.insert({
@@ -57,12 +78,36 @@ async function save(readable: stream.Readable, name: string, type: string, hash:
// Get MongoDB GridFS bucket
const bucket = await getDriveFileBucket();
- return new Promise<IDriveFile>((resolve, reject) => {
- const writeStream = bucket.openUploadStream(name, { contentType: type, metadata });
+ const file = await new Promise<IDriveFile>((resolve, reject) => {
+ const writeStream = bucket.openUploadStream(name, {
+ contentType: type,
+ metadata
+ });
+
writeStream.once('finish', resolve);
writeStream.on('error', reject);
- readable.pipe(writeStream);
+
+ fs.createReadStream(path).pipe(writeStream);
});
+
+ if (thumbnail) {
+ const thumbnailBucket = await getDriveFileThumbnailBucket();
+
+ await new Promise<IDriveFile>((resolve, reject) => {
+ const writeStream = thumbnailBucket.openUploadStream(name, {
+ contentType: 'image/jpeg',
+ metadata: {
+ originalId: file._id
+ }
+ });
+
+ writeStream.once('finish', resolve);
+ writeStream.on('error', reject);
+ writeStream.end(thumbnail);
+ });
+ }
+
+ return file;
}
}
@@ -321,7 +366,7 @@ export default async function(
}
}
} else {
- driveFile = await (save(fs.createReadStream(path), detectedName, mime, hash, size, metadata));
+ driveFile = await (save(path, detectedName, mime, hash, size, metadata));
}
log(`drive file has been created ${driveFile._id}`);
diff --git a/src/services/drive/delete-file.ts b/src/services/drive/delete-file.ts
index 5494023f46..a417d260fa 100644
--- a/src/services/drive/delete-file.ts
+++ b/src/services/drive/delete-file.ts
@@ -6,8 +6,14 @@ import config from '../../config';
export default async function(file: IDriveFile, isExpired = false) {
if (file.metadata.storage == 'minio') {
const minio = new Minio.Client(config.drive.config);
+
const obj = `${config.drive.prefix}/${file.metadata.storageProps.id}`;
await minio.removeObject(config.drive.bucket, obj);
+
+ if (file.metadata.thumbnailUrl) {
+ const thumbnailObj = `${config.drive.prefix}/${file.metadata.storageProps.id}-thumbnail`;
+ await minio.removeObject(config.drive.bucket, thumbnailObj);
+ }
}
// チャンクをすべて削除