AngularJS文件上传,Express和node-postgres(pg)

时间:2019-02-12 17:24:52

标签: angularjs express file-upload node-postgres

在AngularJS中安装了一个组件(即将移植到Angular 7)来更新用户配置文件,该用户配置文件调用AngularJS服务方法以对/ api / user /:id进行PUT。

想添加一张小照片(<100K),与其他字段一起发送到同一PUT中,然后在控制器中处理此类请求...

// route: PUT /api/user/:id
import db from '../../utils/db';

export async function upsert(req, res) {
  const { id, name, phone, email, photo } = req.body;
  // users.photo in PostgreSQL has datatype of bytea
  const sql = `UPDATE users SET name = $2, phone = $3, email = $4, photo = $5) WHERE id = $1 RETURNING id, name, phone, email, photo;`;
  const { rows } = db.query(sql, [id, name, phone, email, photo];
  return res.status(200).send(rows);
}

是否有一种干净的方法来编码图像客户端,以便可以将其包含在AngularJS服务PUT的JSON中?对于该用例,我发现其他解决方案似乎有些过头了-要求图像上传的处理方式与其他字段完全不同。

1 个答案:

答案 0 :(得分:0)

最后被咬住了-使用 formidable node-postgres 为文件及其独立的API创建了单独的表。如果这对其他人有帮助,那么结果如下。

PostgreSQL数据定义...

-- DROP SEQUENCE public.files_seq;
CREATE SEQUENCE IF NOT EXISTS public.files_seq;

-- DROP TABLE public.files;
CREATE TABLE IF NOT EXISTS public.files (
  _id integer PRIMARY KEY DEFAULT nextval('files_seq'::regclass),
  name character varying(512) NOT NULL,
  type character varying(20) NOT NULL,
  data bytea
);

-- DROP INDEX public.users_first_name;
CREATE INDEX files_name ON public.files USING btree (name);

Express的控制器...

import stream from 'stream';
import fs from 'fs';
import { IncomingForm } from 'formidable';
import db from '../../utils/db';

// Returns list of images
export async function index(req, res) {
  const { rows } = await db.query('SELECT _id, name, type FROM files ORDER BY name;', []);
  return res.send(rows);
}

// Uploads a single file
export async function upload(req, res) {
  let _id;
  new IncomingForm().parse(req, (err, fields, files) => {
    if(err) throw err;
    if(Array.isArray(files)) throw new Error('Only one file can be uploaded at a time');
    const { name, type, path } = files.file;
    fs.readFile(path, 'hex', async(err, fileData) => {
      if(err) throw err;
      fileData = `\\x${fileData}`;
      const sql = 'INSERT INTO files (name, type, data) VALUES($1, $2, $3) RETURNING _id;';
      const { rows } = await db.query(sql, [name, type, fileData]);
      _id = rows[0]._id;
      res.send({ id: _id });
      // console.log(`Uploaded ${name} to ${path} and inserted into database (ID = ${_id})`);
      // No need to delete the file uploaded as Heroku has an ephemeral file system
    });
  });
}

// Downloads a file by its _id
export async function download(req, res) {
  const _id = req.params.id;
  const sql = 'SELECT _id, name, type, data FROM files WHERE _id = $1;';
  const { rows } = await db.query(sql, [_id]);
  const file = rows[0];
  const fileContents = Buffer.from(file.data, 'base64');
  const readStream = new stream.PassThrough();
  readStream.end(fileContents);
  res.set('Content-disposition', `attachment; filename=${file.name}`);
  res.set('Content-Type', file.type);
  readStream.pipe(res);
  return rows[0];
}

// Deletes a file from the database (admin-only)
export async function destroy(req, res) {
  const _id = req.params.id;
  const sql = 'DELETE FROM files WHERE _id = $1;';
  await db.query(sql, [_id]);
  res.status(204).send({ message: `File ${_id} deleted.`});
}

在客户端,我正在AngularJS中使用 ng-file-upload

这是该视图的相关部分(为简洁起见,这是哈巴狗)...

.form-group.col-md-6
  label(for='photo') Teacher photo
  input.form-control(ngf-select='$ctrl.uploadPhoto($file)', type='file', id='photo', name='photo', ng-model='$ctrl.user.photo', ngf-pattern="'image/*'", ngf-accept="'image/*'", ngf-max-size='100KB', ngf-min-height='276', ngf-max-height='276', ngf-min-width='236', ngf-max-width='236', ngf-resize='{width: 236, height: 276}', ngf-model-invalid='errorFile')
  ng-messages.help-block.has-error(for='form.photo.$error', ng-show='form.photo.$dirty || form.$submitted', role='alert')
    ng-message(when='maxSize') Please select a photo that is less than 100K.
    ng-message(when='minHeight,maxHeight,minWidth,maxWidth') The image must be 236 x 276 pixels.
  span(ng-if='$ctrl.user.imageId')
    img(ng-src='/api/file/{{ $ctrl.user.imageId }}' alt="Photo of Teacher")

及其控制器中的方法...

  uploadPhoto(file) {
    if(file) {
      this.uploadService.upload({
        url: '/api/file/upload',
        data: { file }
      })
        .then(response => {
          this.user.imageId = response.data.id;
        }, response => {
          if(response.status > 0) console.log(`${response.status}: ${response.data}`);
        }, evt => {
          // Math.min is to fix IE which reports 200% sometimes
          this.uploadProgress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total, 10));
        });
    }
  }