Страницы

Поиск по вопросам

пятница, 20 декабря 2019 г.

Загрузка файла в рамках REST-JSON концепции

#http #rest


Делаем REST-сервис, в качестве транспорта используется JSON (если требуется конкретика
- сервис делается на Java/Spring). Возникла необходимость загружать файлы, и я не понимаю,
как правильно должен выглядеть запрос. В данный момент все данные передаются JSON-массивом
внутри тела запроса без каких-либо параметров, и я не понимаю, можно ли внутри этой
концепции организовать загрузку файлов - в данный момент требуется просто передать
файл, но, возможно, вместе с ним потребуется передавать какие-либо данные - название,
предназначение и т.п. Как это организуется по-человечески?
    


Ответы

Ответ 1



Немного брейнсторминга. Вариант 1 Закачиваем файл POST-ом, метаданные прикладываем как query-параметры. POST /someurl/upload?date=...&comment=... Вариант 2 Выделяем некий ресурс, описывающий метаданные файла: /someurl/document/{id} и субресурс - непосредственно файл: /someurl/document/{id}/data Создаем экземпляр, передавая метаданные. POST /someurl/document { 'date':'2007-03-01T13:00:00Z', 'comment':'....' } Получаем в ответе идентификатор HTTP/1.1 201 Created { 'id': 123456, 'date':'2007-03-01T13:00:00Z', 'comment':'....' } На этом этапе мы уже можем запросить метаданные файла: GET /someurl/document/123456 HTTP/1.1 200 OK { 'id': 123456, 'date':'2007-03-01T13:00:00Z', 'comment':'....' } Но сам файл еще не доступен (субресурс не создан) GET /someurl/document/123456/data HTTP/1.1 404 Not Found Статус 404 это не страшно, он предполагает, что ресурс еще может появиться по данному пути. Заливаем файл POST-ом как субресурс: POST /someurl/document/123456/data Теперь никаких проблем, файл доступен: GET /someurl/document/123456/data HTTP/1.1 200 OK Если хочется 100% жирного REST-а с HATEOAS, то на шаге 3 нужно включить в ответ гипертекстовую ссылку на себя и можно избавиться от идентификатора: { 'date':'2007-03-01T13:00:00Z', 'comment':'....', '_links':{ 'self': { 'href': '/someurl/document/123456' } } } А после шага 4 по запросу метаданных возвращать и ссылку на файл: { 'date':'2007-03-01T13:00:00Z', 'comment':'....', '_links':{ 'self': { 'href': '/someurl/document/123456' }, 'data': { 'href': '/someurl/document/123456/data' }, } } Так у клиентской стороны будет меньше соблазна обратиться к еще не загруженному файлу. Вариант 3 Закачиваем все json-ом. Файл пакуем в base64. Сойдет для небольших файлов. POST /someurl/document { 'date':'2007-03-01T13:00:00Z', 'comment':'....', 'file-data':'SGVsbG8gd29ybGRIZWxsbyB3b3JsZEhlbGxvIHdvcmxkSGVsbG8gd29ybGRIZWxsbyB3b3JsZEhlbGxvIHdvcmxkSGVsbG8gd29ybGRIZWxsbyB3b3JsZEhlbGxvIHdvcmxk' } Вариант 4 Используем multipart-запрос. Описание в json и сам файл - две части. POST /someurl/document Content-Type: multipart/form-data; boundary=spacer --spacer Content-Disposition: form-data; name="metadata" Content-Type: application/json { 'date':'2007-03-01T13:00:00Z', 'comment':'....' } --spacer Content-Disposition: form-data; name="file-data" Content-Type: application/octet-stream Content-Transfer-Encoding: base64 <...байты в base64...> Мне импонирует 2й вариант, как наиболее идеологически верный.

Ответ 2



Еще вариант слать все одним multipart/form-data. JSON в таком случае нужно сконвертировать в соответствующие ключи FormData. Из плюсов это конечно же работа с единой сущностью в один момент времени. Атомарность рулит. И еще один вариант: Сделать ресурс /upload/tmpfile, который бы помещал файл во временную директорию и возращал бы его id из базы данных (или url, что добавило бы универсальности нашему конечному ресурсу). Дальше можно было бы использовать этот id при отправке обычного JSON-a. Минусы все те-же: tmp файлы нужно чистить и появляется дополнительная сущность.

Комментариев нет:

Отправить комментарий