Procházet zdrojové kódy

download zip file for multiple collections

mizozobu před 6 roky
rodič
revize
188ee818e6

+ 1 - 1
src/client/js/components/Admin/Export/ExportAsZip.jsx

@@ -115,7 +115,7 @@ class ExportPage extends React.Component {
           })}
           })}
         </form>
         </form>
         <button type="button" className="btn btn-sm btn-default" onClick={this.exportMultiple}>Generate</button>
         <button type="button" className="btn btn-sm btn-default" onClick={this.exportMultiple}>Generate</button>
-        <a href="/_api/v3/export/pages">
+        <a href="/_api/v3/export/download">
           <button type="button" className="btn btn-sm btn-primary ml-2">Download</button>
           <button type="button" className="btn btn-sm btn-primary ml-2">Download</button>
         </a>
         </a>
         {/* <button type="button" className="btn btn-sm btn-danger ml-2" onClick={this.deleteZipFile}>Clear</button> */}
         {/* <button type="button" className="btn btn-sm btn-danger ml-2" onClick={this.deleteZipFile}>Clear</button> */}

+ 63 - 12
src/server/routes/apiv3/export.js

@@ -25,6 +25,12 @@ module.exports = (crowi) => {
    *      description: get mongodb collections names and zip files for them
    *      description: get mongodb collections names and zip files for them
    *      produces:
    *      produces:
    *        - application/json
    *        - application/json
+   *      parameters:
+   *        - name: collectionName
+   *          in: path
+   *          description: collection name
+   *          schema:
+   *            type: string
    *      responses:
    *      responses:
    *        200:
    *        200:
    *          description: export cache info
    *          description: export cache info
@@ -41,10 +47,10 @@ module.exports = (crowi) => {
   /**
   /**
    * @swagger
    * @swagger
    *
    *
-   *  /export/:collection:
+   *  /export/download:
    *    get:
    *    get:
    *      tags: [Export]
    *      tags: [Export]
-   *      description: download a zipped json for page collection
+   *      description: download a zipped json for multiple collections
    *      produces:
    *      produces:
    *        - application/json
    *        - application/json
    *      responses:
    *      responses:
@@ -53,10 +59,43 @@ module.exports = (crowi) => {
    *          content:
    *          content:
    *            application/zip:
    *            application/zip:
    */
    */
-  router.get('/:collection', async(req, res) => {
+  router.get('/download', async(req, res) => {
     // TODO: add express validator
     // TODO: add express validator
     try {
     try {
-      const { collection: collectionName } = req.params;
+      return res.download(exportService.zipFile);
+    }
+    catch (err) {
+      // TODO: use ApiV3Error
+      logger.error(err);
+      return res.status(500).send({ status: 'ERROR' });
+    }
+  });
+
+  /**
+   * @swagger
+   *
+   *  /export/:collectionName:
+   *    get:
+   *      tags: [Export]
+   *      description: download a zipped json for a single collection
+   *      produces:
+   *        - application/json
+   *      parameters:
+   *        - name: collectionName
+   *          in: path
+   *          description: collection name
+   *          schema:
+   *            type: string
+   *      responses:
+   *        200:
+   *          description: a zip file
+   *          content:
+   *            application/zip:
+   */
+  router.get('/:collectionName', async(req, res) => {
+    // TODO: add express validator
+    try {
+      const { collectionName } = req.params;
       // get model for collection
       // get model for collection
       const Model = exportService.getModelFromCollectionName(collectionName);
       const Model = exportService.getModelFromCollectionName(collectionName);
       // get zip file path
       // get zip file path
@@ -74,22 +113,28 @@ module.exports = (crowi) => {
   /**
   /**
    * @swagger
    * @swagger
    *
    *
-   *  /export/pages:
+   *  /export/:collectionName:
    *    post:
    *    post:
    *      tags: [Export]
    *      tags: [Export]
-   *      description: generate a zipped json for page collection
+   *      description: generate a zipped json for a single collection
    *      produces:
    *      produces:
    *        - application/json
    *        - application/json
+   *      parameters:
+   *        - name: collectionName
+   *          in: path
+   *          description: collection name
+   *          schema:
+   *            type: string
    *      responses:
    *      responses:
    *        200:
    *        200:
    *          description: a zip file is generated
    *          description: a zip file is generated
    *          content:
    *          content:
    *            application/json:
    *            application/json:
    */
    */
-  router.post('/:collection', async(req, res) => {
+  router.post('/:collectionName', async(req, res) => {
     // TODO: add express validator
     // TODO: add express validator
     try {
     try {
-      const { collection: collectionName } = req.params;
+      const { collectionName } = req.params;
       // get model for collection
       // get model for collection
       const Model = exportService.getModelFromCollectionName(collectionName);
       const Model = exportService.getModelFromCollectionName(collectionName);
       // export into json
       // export into json
@@ -155,22 +200,28 @@ module.exports = (crowi) => {
   /**
   /**
    * @swagger
    * @swagger
    *
    *
-   *  /export/:collection:
+   *  /export/:collectionName:
    *    delete:
    *    delete:
    *      tags: [Export]
    *      tags: [Export]
-   *      description: unlink a json and zip file for page collection
+   *      description: unlink a json and zip file for a single collection
    *      produces:
    *      produces:
    *        - application/json
    *        - application/json
+   *      parameters:
+   *        - name: collectionName
+   *          in: path
+   *          description: collection name
+   *          schema:
+   *            type: string
    *      responses:
    *      responses:
    *        200:
    *        200:
    *          description: the json and zip file are removed
    *          description: the json and zip file are removed
    *          content:
    *          content:
    *            application/json:
    *            application/json:
    */
    */
-  // router.delete('/:collection', async(req, res) => {
+  // router.delete('/:collectionName', async(req, res) => {
   //   // TODO: add express validator
   //   // TODO: add express validator
   //   try {
   //   try {
-  //     const { collection: collectionName } = req.params;
+  //     const { collectionName } = req.params;
   //     // get model for collection
   //     // get model for collection
   //     const Model = exportService.getModelFromCollectionName(collectionName);
   //     const Model = exportService.getModelFromCollectionName(collectionName);
   //     // remove .json and .zip for collection
   //     // remove .json and .zip for collection

+ 15 - 5
src/server/service/export.js

@@ -13,6 +13,9 @@ class ExportService {
     this.per = 100;
     this.per = 100;
     this.zlibLevel = 9; // 0(min) - 9(max)
     this.zlibLevel = 9; // 0(min) - 9(max)
 
 
+    // path to zip file for exporting multiple collection
+    this.zipFile = path.join(this.baseDir, 'GROWI.zip');
+
     // { pages: Page, users: User, ... }
     // { pages: Page, users: User, ... }
     this.collectionMap = {};
     this.collectionMap = {};
     this.initCollectionMap(crowi.models);
     this.initCollectionMap(crowi.models);
@@ -157,7 +160,7 @@ class ExportService {
    * @param {string} from path to input file
    * @param {string} from path to input file
    * @param {string} [to=`${path.join(path.dirname(from), `${path.basename(from, path.extname(from))}.zip`)}`] path to output file
    * @param {string} [to=`${path.join(path.dirname(from), `${path.basename(from, path.extname(from))}.zip`)}`] path to output file
    * @param {string} [as=path.basename(from)] file name after unzipped
    * @param {string} [as=path.basename(from)] file name after unzipped
-   * @return {object} file path and file size
+   * @return {string} path to zip file
    * @see https://www.archiverjs.com/#quick-start
    * @see https://www.archiverjs.com/#quick-start
    */
    */
   async zipSingleFile(from, to = this.replaceExtension(from, 'zip'), as = path.basename(from)) {
   async zipSingleFile(from, to = this.replaceExtension(from, 'zip'), as = path.basename(from)) {
@@ -193,11 +196,18 @@ class ExportService {
     return to;
     return to;
   }
   }
 
 
-  async zipMultipleFiles(configs, to = path.join(this.baseDir, `${this.appService.getAppTitle()}-${(new Date()).getTime()}.zip`)) {
+  /**
+   * zip a file
+   *
+   * @memberOf ExportService
+   * @param {array} configs array of object { from: "path to source file", as: "file name appears after unzipped" }
+   * @return {string} path to zip file
+   */
+  async zipMultipleFiles(configs) {
     const archive = archiver('zip', {
     const archive = archiver('zip', {
       zlib: { level: this.zlibLevel },
       zlib: { level: this.zlibLevel },
     });
     });
-    const output = fs.createWriteStream(to);
+    const output = fs.createWriteStream(this.zipFile);
 
 
     // good practice to catch warnings (ie stat failures and other non-blocking errors)
     // good practice to catch warnings (ie stat failures and other non-blocking errors)
     archive.on('warning', (err) => {
     archive.on('warning', (err) => {
@@ -224,9 +234,9 @@ class ExportService {
 
 
     await streamToPromise(archive);
     await streamToPromise(archive);
 
 
-    logger.debug(`zipped growi data into ${to} (${archive.pointer()} bytes)`);
+    logger.debug(`zipped growi data into ${this.zipFile} (${archive.pointer()} bytes)`);
 
 
-    return to;
+    return this.zipFile;
   }
   }
 
 
   /**
   /**