Как сохранить файл в папке с помощью FileSystem и CollectionFS? (да, действительно.)

Я думаю, что я что-то упускаю. Я прочитал много сообщений/примеров и не могу сохранять изображения в своей системе (я работаю локально).

Какова моя цель?

Я пытаюсь сохранить файл, отправленный пользователем, в папку (на стороне сервера). Звучит легко? Может быть.

В чем проблема ?

Краткий ответ: я не могу понять, как сохранить файл в моей папке. Хотите больше информации?

История загрузки файла

Я читал, что для использования параметра path, такого как new FS.Store.FileSystem("thumb", { path: "/public/images/user/avatar" }) , я должен объявить свою коллекцию на стороне сервера. Но когда я звоню Avatars.insert() (Аватары — это название моей коллекции), кажется, что его не существует. Это имеет смысл, потому что эта коллекция существует только на сервере.

Итак, я попытался объявить коллекцию как на стороне сервера, так и на стороне клиента (я читал несколько примеров об этом), и это работает! Файл правильно добавлен в MongoDB, но моя папка все еще пуста (я не уверен, но я думаю, что это потому, что Avatars.insert() называется клиентской стороной, поэтому используемая коллекция является клиентской, той, которая не может принимать параметр path ).

Без проблем ! Я создал 2 метода Meteor (один на стороне клиента и один на стороне сервера) под названием «updateAvatarFile». С помощью этого «трюка» я могу сделать Meteor.call("updateAvatarFile", field.files[0]), который вызывает как серверные, так и клиентские методы. Так что я могу делать некоторые вещи с пользовательским интерфейсом на стороне клиента и загружать файл в другом. Но я не могу передать файл в качестве параметра.

field.files[0] содержит файл на стороне клиента, но на стороне сервера это пустой объект. Мой вопрос: как я могу загрузить файл?

Я не могу сделать это на стороне клиента (поскольку я не могу использовать параметр path), но я могу передать файл на сервер. Я уверен, что что-то упускаю, но не могу понять что.

Вот как я иду:

// /client/views/templates/settings.js
Template.settings.events({
   'submit #updateAvatar': function (e, template) {
        e.preventDefault();
        const field = document.getElementsByName('avatar')[0];
        Meteor.call('updateAvatarFile', field.files[0]);
    }
});

// /client/lib/clientMethods.js
Meteor.methods({
    'updateAvatarFile': function (file) {
        // blabla
    }
});

// /server/lib/serverMethods.js
Meteor.methods({
    'updateAvatarFile': function (file) {
        Avatars.insert(file, function (err, fileObj) {
            if (err) {
                console.log(err);
            } else {
                console.log(fileObj);
            }
        });
    }
});

// /server/collections/serverAvatarCollection.js
Avatars = new FS.Collection("avatars", {
    stores: [
        new FS.Store.FileSystem("original", { path: "/public/images/user/avatar" }),
        new FS.Store.FileSystem("thumb", { path: "/public/images/user/avatar" })
    ],
    filter: {
        maxSize: 1000000, //1Mo
        allow: { contentTypes: ['image/*'] }
    },
    onInvalid: function (message) {
        //throw new Meteor.Error(403, message);
    }
});

// /client/collections/clientAvatarCollection.js
// (this one is actually in a comment block)
Avatars = new FS.Collection("avatars", {
    stores: [
        new FS.Store.FileSystem("original"),
        new FS.Store.FileSystem("thumb")
    ],
    filter: {
        maxSize: 1000000, //1Mo
        allow: { contentTypes: ['image/*'] }
    },
    onInvalid: function (message) {
        alert(message);
    }
});

Я также пытался вставить файл с помощью метода на стороне клиента, но у меня тот же результат (файл добавляется в MongoDB, но не сохраняется в папке).

Использование разных значений path тоже не сработало.


РЕДАКТИРОВАТЬ: Или, может быть, я пытаюсь использовать не тот пакет? На мой взгляд, преобразование изображения в куски и сохранение их в MongoDB звучит очень странно и плохо. Есть ли у вас какие-либо советы?


EDIT 2: ответ Мишелю Флойду (извините, ограничение на количество символов раздражает).

Во-первых, спасибо за ваш ответ!

1. В данный момент я просто пробую Meteor, поэтому у меня установлены и autopublish, и insecure. Отсутствие публикации/подписки на мою коллекцию не может вызвать проблемы, верно?

2. До вашего ответа я попытался настроить коллекцию, доступную как для сервера, так и для клиента, поместив avatarCollection.js в /collections. Я думал, что путь, который не содержит сервер или клиент, автоматически доступен для обеих сторон. Так в чем разница между /collections и /lib ? (Я знаю, что все файлы в папке «lib» загружаются первыми). Неправильно ли размещать коллекции в /collections ? Может быть, мне создать папку /lib/collections?

3. (последний пункт, извините за длинный комментарий) Я попробовал то, что вы посоветовали выше, но это не работает (или я делаю что-то не так, опять >‹). Когда я использую Avatars.insert(), CollectionFS не сохраняет файл в моем локальном хранилище. Я также проверил корень моего жесткого диска на случай, если CollectionFS интерпретирует / как корень моей машины, но это не так. С другой стороны, CollectionFS создала 4 коллекции в MongoDB (cfs._tempstore.chunks, cfs.avatars.filerecord, cfs_gridfs._tempstore.chunks и cfs_gridfs._tempstore.files) — gridfs выглядит странно. У меня установлена ​​GridFS, но я использую FileSystem -. Эти таблицы не пусты. Вот почему я думаю, что CollectionFS разделит мой файл на куски и сохранит их в MongoDB.


person Armelias    schedule 30.10.2015    source источник


Ответы (1)


Вы в целом на правильном пути. CollectionFS использует адаптеры хранения для фактического хранения файлов. Вы можете размещать файлы на S3, gridFS или в вашей локальной файловой системе, как вы пытаетесь это сделать. Обычно избегают прямого помещения содержимого файла в Mongo.

Во-первых, определите свою коллекцию:

Avatars = new FS.Collection("avatars", {
    stores: [
        new FS.Store.FileSystem("original", { path: "/public/images/user/avatar" }),
        new FS.Store.FileSystem("thumb", { path: "/public/images/user/avatar" })
    ],
    filter: {
        maxSize: 1000000, //1Mo
        allow: { contentTypes: ['image/*'] }
    },
    onInvalid: function (message) {
        //throw new Meteor.Error(403, message);
    }
});

в /lib! Это сделает его доступным как для сервера, так и для клиента.

Во-вторых, убедитесь, что вы опубликовали свою коллекцию аватаров с сервера и подписались на нее с клиента. Я не вижу кода публикации/подписки в вашем вопросе. Вы нуждаетесь в этом.

В-третьих, если вы просто сделаете:

Avatars.insert(...);

на клиенте с файлом, тогда CollectionFS затем CollectionFS позаботится о его сохранении для вас. Дело в том, что он не будет доступен мгновенно. Фактическая загрузка и сохранение могут занять некоторое время. Например, вы можете посмотреть на fileObj.isUploaded, чтобы узнать, готов ли файл.

person Michel Floyd    schedule 30.10.2015
comment
Большое спасибо за ответ. Я отредактировал свой исходный пост, чтобы ответить. - person Armelias; 31.10.2015
comment
1. если у вас включена автопубликация, то публикация не ваша проблема. 2. Вы правы в том, что Meteor отправляет файлы в неспециальные каталоги как клиенту, так и серверу. 3. Каталоги tempstore используются только временно, поскольку фрагменты файлов перемещаются в адаптер хранения. - person Michel Floyd; 02.11.2015
comment
Извините за задержку. Дело в том, что фрагменты файлов не попадают в адаптер хранения (FileSystem). Моя папка пуста, событие после того, как что-то вставил в мою коллекцию. - person Armelias; 09.11.2015
comment
Вы можете попробовать попросить ребят из CollectionFS напрямую в проекте github об адаптере файловой системы. Я использовал его с S3 и локальной файловой системой, и он всегда работал. - person Michel Floyd; 10.11.2015