Skip to content

LINE to Google Drive Integration#

Learn how to automatically transfer files from LINE messaging platform to Google Drive with smart organization and processing capabilities.

🎯 What You'll Build#

A file automation system that: - Receives files (images, documents, videos) via LINE messaging - Automatically downloads and categorizes files - Organizes files in Google Drive with intelligent folder structure - Processes and analyzes file content - Sends confirmation notifications back to LINE - Maintains file inventory and metadata

πŸ“‹ Requirements#

  • LINE Developers account with Messaging API
  • Google Cloud project with Drive API enabled
  • Google Service Account for Drive access
  • n8n instance running with internet access

πŸ”§ Workflow Overview#

Key Components#

  1. LINE Webhook Trigger - Receives messages and files
  2. File Downloader - Downloads files from LINE servers
  3. File Analyzer - Determines file type and metadata
  4. Drive Uploader - Uploads to Google Drive with organization
  5. Notification Sender - Confirms successful transfer
  6. Database Logger - Tracks file transfers and metadata

πŸ“ Step-by-Step Guide#

1. Set Up LINE Integration#

  1. Configure LINE Developers Console - Create Messaging API channel - Enable webhook endpoint - Get Channel Access Token - Configure webhook URL to your n8n instance

  2. Set Up LINE Trigger

    1
    2
    3
    4
    5
    6
    // LINE Webhook Trigger Configuration
    {
      webhookUrl: 'https://your-n8n-instance.com/webhook/line-drive',
      channelAccessToken: 'YOUR_CHANNEL_ACCESS_TOKEN',
      responseMode: 'respondOnReceive'
    }
    

2. Handle File Messages#

  1. Detect File Messages

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    // Process incoming LINE messages
    function processLineMessage(event) {
      if (event.message.type === 'image') {
        return {
          type: 'image',
          messageId: event.message.id,
          userId: event.source.userId,
          timestamp: event.timestamp
        };
      } else if (event.message.type === 'file') {
        return {
          type: 'file',
          messageId: event.message.id,
          fileName: event.message.fileName,
          fileSize: event.message.fileSize,
          userId: event.source.userId,
          timestamp: event.timestamp
        };
      } else if (event.message.type === 'video') {
        return {
          type: 'video',
          messageId: event.message.id,
          duration: event.message.duration,
          userId: event.source.userId,
          timestamp: event.timestamp
        };
      }
    
      return null; // Not a file message
    }
    

  2. Download Files from LINE

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // Download file content from LINE servers
    async function downloadFileFromLine(messageId, type) {
      const lineApiUrl = 'https://api-data.line.me/v2/bot/message/' + messageId + '/content';
    
      try {
        const response = await fetch(lineApiUrl, {
          headers: {
            'Authorization': 'Bearer ' + process.env.LINE_CHANNEL_ACCESS_TOKEN
          }
        });
    
        if (!response.ok) {
          throw new Error(`LINE API Error: ${response.status} ${response.statusText}`);
        }
    
        const buffer = await response.arrayBuffer();
        return Buffer.from(buffer);
      } catch (error) {
        console.error('Error downloading file from LINE:', error);
        throw error;
      }
    }
    

3. File Analysis and Categorization#

  1. Determine File Type and Category

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    // Analyze file and determine appropriate category
    function analyzeFile(fileBuffer, fileInfo) {
      const analysis = {
        originalType: fileInfo.type,
        size: fileBuffer.length,
        timestamp: new Date().toISOString()
      };
    
      // Enhanced file type detection
      if (fileInfo.type === 'image') {
        analysis.category = 'Images';
        analysis.subcategory = 'Photos';
    
        // Detect image dimensions (if possible)
        const dimensions = getImageDimensions(fileBuffer);
        if (dimensions) {
          analysis.width = dimensions.width;
          analysis.height = dimensions.height;
        }
      } else if (fileInfo.type === 'file') {
        const extension = fileInfo.fileName.split('.').pop().toLowerCase();
    
        if (['pdf', 'doc', 'docx', 'txt'].includes(extension)) {
          analysis.category = 'Documents';
          analysis.subcategory = 'Text Documents';
        } else if (['xls', 'xlsx', 'csv'].includes(extension)) {
          analysis.category = 'Documents';
          analysis.subcategory = 'Spreadsheets';
        } else if (['ppt', 'pptx'].includes(extension)) {
          analysis.category = 'Documents';
          analysis.subcategory = 'Presentations';
        } else {
          analysis.category = 'Other Files';
          analysis.subcategory = 'General';
        }
      } else if (fileInfo.type === 'video') {
        analysis.category = 'Videos';
        analysis.subcategory = 'General';
        analysis.duration = fileInfo.duration;
      }
    
      return analysis;
    }
    

  2. Generate File Metadata

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // Create comprehensive file metadata
    function generateFileMetadata(analysis, lineInfo) {
      return {
        // Source information
        source: 'LINE',
        userId: lineInfo.userId,
        originalMessageId: lineInfo.messageId,
        receivedAt: new Date(lineInfo.timestamp).toISOString(),
    
        // File information
        category: analysis.category,
        subcategory: analysis.subcategory,
        fileSize: analysis.size,
        originalFileName: lineInfo.fileName || `${lineInfo.messageId}.${getFileExtension(analysis.originalType)}`,
    
        // Processing information
        processedAt: analysis.timestamp,
        processingVersion: '1.0',
    
        // Additional metadata
        tags: generateTags(analysis, lineInfo),
        description: generateDescription(analysis)
      };
    }
    

4. Google Drive Integration#

  1. Organize Folder Structure

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // Create organized folder structure
    function generateFolderPath(metadata) {
      const now = new Date();
      const year = now.getFullYear();
      const month = String(now.getMonth() + 1).padStart(2, '0');
      const day = String(now.getDate()).padStart(2, '0');
    
      // Create user-specific folder if available
      const userFolder = metadata.userId ? `user_${metadata.userId}` : 'anonymous';
    
      return {
        basePath: `LINE Files`,
        yearPath: `${year}`,
        monthPath: `${year}-${month}`,
        categoryPath: metadata.category,
        userPath: userFolder,
        fullPath: `LINE Files/${year}/${year}-${month}/${metadata.category}/${userFolder}`
      };
    }
    

  2. Upload to Google Drive

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    // Upload file to Google Drive with metadata
    async function uploadToGoogleDrive(fileBuffer, metadata) {
      const { google } = require('googleapis');
      const auth = new google.auth.GoogleAuth({
        keyFile: 'path/to/service-account.json',
        scopes: ['https://www.googleapis.com/auth/drive']
      });
    
      const drive = google.drive({ version: 'v3', auth });
      const folderPath = generateFolderPath(metadata);
    
      // Ensure folders exist
      const folderId = await ensureFolderStructure(drive, folderPath);
    
      // Upload file
      const fileMetadata = {
        name: sanitizeFileName(metadata.originalFileName),
        parents: [folderId],
        appProperties: {
          source: 'LINE',
          userId: metadata.userId,
          category: metadata.category
        },
        properties: {
          originalMessageId: metadata.originalMessageId,
          receivedAt: metadata.receivedAt,
          processedAt: metadata.processedAt
        }
      };
    
      const media = {
        mimeType: getMimeType(metadata.originalFileName),
        body: require('stream').Readable.from(fileBuffer)
      };
    
      try {
        const response = await drive.files.create({
          resource: fileMetadata,
          media: media,
          fields: 'id,name,webViewLink,size,createdTime'
        });
    
        return {
          fileId: response.data.id,
          fileName: response.data.name,
          viewUrl: response.data.webViewLink,
          size: response.data.size,
          createdTime: response.data.createdTime
        };
      } catch (error) {
        console.error('Google Drive upload error:', error);
        throw error;
      }
    }
    

  3. Folder Structure Management

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    // Ensure folder structure exists in Google Drive
    async function ensureFolderStructure(drive, folderPath) {
      const pathParts = folderPath.fullPath.split('/');
      let currentFolderId = 'root'; // Start from root
    
      for (const folderName of pathParts) {
        // Check if folder exists
        const existingFolder = await findFolderByName(drive, folderName, currentFolderId);
    
        if (existingFolder) {
          currentFolderId = existingFolder.id;
        } else {
          // Create new folder
          const newFolder = await drive.files.create({
            resource: {
              name: folderName,
              mimeType: 'application/vnd.google-apps.folder',
              parents: [currentFolderId]
            },
            fields: 'id'
          });
          currentFolderId = newFolder.data.id;
        }
      }
    
      return currentFolderId;
    }
    
    async function findFolderByName(drive, folderName, parentId) {
      try {
        const response = await drive.files.list({
          q: `name='${folderName}' and mimeType='application/vnd.google-apps.folder' and trashed=false and '${parentId}' in parents`,
          fields: 'files(id,name)'
        });
    
        return response.data.files.length > 0 ? response.data.files[0] : null;
      } catch (error) {
        console.error('Error finding folder:', error);
        return null;
      }
    }
    

5. Smart File Processing#

  1. Image Processing

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    // Process images with additional features
    async function processImage(fileBuffer, metadata) {
      const sharp = require('sharp');
    
      try {
        const image = sharp(fileBuffer);
        const metadata_image = await image.metadata();
    
        // Generate thumbnail
        const thumbnail = await image
          .resize(200, 200, { fit: 'inside' })
          .jpeg({ quality: 80 })
          .toBuffer();
    
        // Extract EXIF data if available
        let exifData = null;
        if (metadata_image.exif) {
          exifData = await extractExifData(fileBuffer);
        }
    
        return {
          originalWidth: metadata_image.width,
          originalHeight: metadata_image.height,
          format: metadata_image.format,
          thumbnail: thumbnail,
          exifData: exifData,
          dominantColor: await extractDominantColor(fileBuffer)
        };
      } catch (error) {
        console.error('Image processing error:', error);
        return null;
      }
    }
    

  2. Document Processing

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    // Process documents for text extraction
    async function processDocument(fileBuffer, metadata) {
      const pdfParse = require('pdf-parse');
      const mammoth = require('mammoth');
    
      try {
        let textContent = '';
        const extension = metadata.originalFileName.split('.').pop().toLowerCase();
    
        if (extension === 'pdf') {
          const data = await pdfParse(fileBuffer);
          textContent = data.text;
        } else if (extension === 'docx') {
          const result = await mammoth.extractRawText({ buffer: fileBuffer });
          textContent = result.value;
        } else if (extension === 'txt') {
          textContent = fileBuffer.toString('utf-8');
        }
    
        // Extract key information from text
        const extractedInfo = await extractKeyInformation(textContent);
    
        return {
          textContent: textContent.substring(0, 1000), // First 1000 chars
          wordCount: textContent.split(' ').length,
          extractedInfo: extractedInfo
        };
      } catch (error) {
        console.error('Document processing error:', error);
        return null;
      }
    }
    

6. Notification System#

  1. Send Confirmation to LINE

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // Send confirmation message back to user
    async function sendLineConfirmation(userId, uploadResult, metadata) {
      const confirmationMessage = {
        type: 'text',
        text: `βœ… File uploaded successfully!\n\n` +
              `πŸ“ File: ${metadata.originalFileName}\n` +
              `πŸ“‚ Category: ${metadata.category}\n` +
              `πŸ“Š Size: ${formatFileSize(metadata.size)}\n` +
              `πŸ”— View: ${uploadResult.viewUrl}\n\n` +
              `File has been organized in your Google Drive!`
      };
    
      // Add thumbnail for images
      if (metadata.category === 'Images' && uploadResult.thumbnail) {
        const lineApiUrl = 'https://api-data.line.me/v2/bot/message/upload';
        // Upload thumbnail and create rich message
      }
    
      await sendLineMessage(userId, confirmationMessage);
    }
    

  2. Send Rich Messages

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    // Create rich message with image preview
    function createRichMessage(uploadResult, metadata) {
      if (metadata.category === 'Images') {
        return {
          type: 'image',
          originalContentUrl: uploadResult.viewUrl,
          previewImageUrl: uploadResult.thumbnailUrl || uploadResult.viewUrl
        };
      } else {
        return {
          type: 'template',
          altText: 'File uploaded successfully',
          template: {
            type: 'buttons',
            text: `File "${metadata.originalFileName}" uploaded to Google Drive`,
            actions: [
              {
                type: 'uri',
                label: 'View File',
                uri: uploadResult.viewUrl
              },
              {
                type: 'postback',
                label: 'Share',
                data: `action=share&fileId=${uploadResult.fileId}`
              }
            ]
          }
        };
      }
    }
    

πŸ“Š Advanced Features#

Duplicate Detection#

  1. File Hashing
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    // Generate file hash for duplicate detection
    const crypto = require('crypto');
    
    function generateFileHash(fileBuffer) {
      return crypto.createHash('sha256').update(fileBuffer).digest('hex');
    }
    
    // Check for duplicates
    async function checkForDuplicates(fileHash, drive, metadata) {
      const query = `appProperties has { key='fileHash' and value='${fileHash}' } and trashed=false`;
    
      try {
        const response = await drive.files.list({
          q: query,
          fields: 'files(id,name,createdTime)'
        });
    
        if (response.data.files.length > 0) {
          return {
            isDuplicate: true,
            existingFile: response.data.files[0]
          };
        }
    
        return { isDuplicate: false };
      } catch (error) {
        console.error('Duplicate check error:', error);
        return { isDuplicate: false };
      }
    }
    

File Sharing Automation#

  1. Smart Sharing Rules
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    // Implement smart sharing based on content
    async function applySharingRules(uploadResult, metadata, processedContent) {
      const { google } = require('googleapis');
      const auth = new google.auth.GoogleAuth({
        keyFile: 'path/to/service-account.json',
        scopes: ['https://www.googleapis.com/auth/drive']
      });
    
      const drive = google.drive({ version: 'v3', auth });
    
      // Define sharing rules
      const sharingRules = [
        {
          condition: (meta) => meta.category === 'Documents' && meta.subcategory === 'Spreadsheets',
          action: 'shareWithTeam',
          emails: ['[email protected]']
        },
        {
          condition: (meta) => meta.tags.includes('invoice'),
          action: 'shareWithFinance',
          emails: ['[email protected]']
        }
      ];
    
      for (const rule of sharingRules) {
        if (rule.condition(metadata)) {
          await shareFile(drive, uploadResult.fileId, rule.emails, 'reader');
        }
      }
    }
    
    async function shareFile(drive, fileId, emails, role) {
      for (const email of emails) {
        await drive.permissions.create({
          fileId: fileId,
          requestBody: {
            type: 'user',
            role: role,
            emailAddress: email
          }
        });
      }
    }
    

Content Analysis#

  1. AI-Powered Content Analysis
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // Analyze image content with AI
    async function analyzeImageContent(imageBuffer) {
      const vision = require('@google-cloud/vision');
      const client = new vision.ImageAnnotatorClient();
    
      try {
        const [result] = await client.labelDetection(imageBuffer);
        const labels = result.labelAnnotations.map(label => ({
          description: label.description,
          confidence: label.score
        }));
    
        const [textResult] = await client.textDetection(imageBuffer);
        const text = textResult.textAnnotations[0]?.description || '';
    
        return {
          labels: labels,
          extractedText: text,
          safeSearch: (await client.safeSearchDetection(imageBuffer))[0].safeSearchAnnotation
        };
      } catch (error) {
        console.error('Vision API error:', error);
        return null;
      }
    }
    

πŸ§ͺ Testing and Monitoring#

Test Scenarios#

  1. File Upload Testing
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    // Test different file types
    const testFiles = [
      { type: 'image', url: 'https://example.com/test.jpg' },
      { type: 'document', url: 'https://example.com/test.pdf' },
      { type: 'video', url: 'https://example.com/test.mp4' }
    ];
    
    async function testFileUpload(testFile) {
      const startTime = Date.now();
      try {
        const result = await processFileUpload(testFile);
        const duration = Date.now() - startTime;
    
        return {
          success: true,
          duration: duration,
          result: result
        };
      } catch (error) {
        return {
          success: false,
          duration: Date.now() - startTime,
          error: error.message
        };
      }
    }
    

Performance Monitoring#

  1. Upload Metrics
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    // Track upload performance
    function trackUploadMetrics(uploadResult, metadata, processingTime) {
      const metrics = {
        timestamp: new Date().toISOString(),
        fileSize: metadata.size,
        processingTime: processingTime,
        category: metadata.category,
        success: uploadResult.success,
        error: uploadResult.error || null
      };
    
      // Send to monitoring system
      sendMetricsToMonitoring(metrics);
    
      return metrics;
    }
    

πŸ” Troubleshooting#

Common Issues#

LINE API Errors - Check webhook URL is accessible - Verify Channel Access Token - Monitor rate limits - Check message format

Google Drive Issues - Verify service account permissions - Check Drive API quota - Validate folder structure - Handle large file uploads

File Processing Errors - Unsupported file formats - Corrupted files - Memory issues with large files - Encoding problems

Debug Tools#

  1. Comprehensive Logging
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    // Add detailed logging
    function logFileProcessing(metadata, stage, data, error = null) {
      const logEntry = {
        timestamp: new Date().toISOString(),
        messageId: metadata.originalMessageId,
        stage: stage,
        userId: metadata.userId,
        data: data,
        error: error ? error.message : null
      };
    
      console.log(JSON.stringify(logEntry));
      // Optionally send to logging service
    }
    

πŸ“ˆ Scaling Considerations#

High Volume Processing#

  1. Queue System

    1
    2
    3
    4
    5
    6
    7
    8
    // Implement job queue for processing
    const Queue = require('bull');
    const fileProcessingQueue = new Queue('file processing');
    
    fileProcessingQueue.process(async (job) => {
      const { metadata, fileBuffer } = job.data;
      return await processFile(metadata, fileBuffer);
    });
    

  2. Caching Strategy

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    // Cache frequently accessed data
    const NodeCache = require('node-cache');
    const cache = new NodeCache({ stdTTL: 600 }); // 10 minutes
    
    function getCachedFolderStructure(path) {
      return cache.get(path);
    }
    
    function setCachedFolderStructure(path, folderId) {
      cache.set(path, folderId);
    }
    


Related Tutorials: - LINE Messaging API - Basic LINE integration - Google Sheets Integration - Google services setup

Resources: - LINE Messaging API Documentation - Google Drive API Documentation - n8n LINE Integration