google drive使用教程

250 阅读4分钟

google drive使用教程

api官方文档

developers.google.cn/workspace/d…

需要配置SCOPES, CLIENT_ID, API_KEY, Project_Number

SCOPES

drive官方权限文档

developers.google.cn/workspace/d…

调用picker的所需的权限配置

console.cloud.google.com/auth/scopes

增加权限下面两个权限

www.googleapis.com/auth/drive.…

www.googleapis.com/auth/drive.…

在这里插入图片描述

apikey

console.cloud.google.com/apis/creden…

配置apikey需要权限

Google Photos Picker API Google Picker API Google Drive API (这个是选择文件后使用api)

API和服务-->库 中搜索选择对应api启用

注:由于这个api key放在前端使用尽量粒度最小 CLIENT_ID console.cloud.google.com/apis/creden…

oauth2的clientid

需要设置授权域名如:www.example.com

本地域名只能使用localhost,记得加进去

AppId

使用 Cloud 项目编号设置云端硬盘应用 ID

console.cloud.google.com/home/dashbo…里的Project number (项目编号)

纯数字来的,不是project ID(项目 ID)

在这里插入图片描述

注:此文档仅限web自身浏览器交互不涉及后端调用drive接口

示例代码:

      
<template>
  <div class="pannel-wrap">
    <div class="upload-box w-full" @click="handleUpload">
      <drag-fullscreen :limit="limit" :before="false" @uploadFile="onFileInputChange" />
      <input
        id="hide-input"
        ref="uploadInput"
        type="file"
        :multiple="limit > 1"
        :accept="accept"
        @change="onFileInputChange"
      />
      <div class="upload-content h-full column-center">
        <!-- <div class="button" /> -->
        <i-icon class="upload-icon" icon="upload" />
        <p class="desc">{{ $t(`components.pannel.${$store.state.isH5 ? 'descH5' : 'descPc'}`) }}</p>
        <div class="or row-center">{{ $t('components.pannel.or') }}</div>
        <div class="google-drive" @click.stop="handleGoogleDriveUpload">
          <i-icon class="icon" icon="google-drive" />
          <p class="text">{{ $t('components.pannel.desc2') }}</p>
        </div>
        <p class="tip">{{ $t('components.pannel.tip') }}</p>
      </div>
    </div>
    <div class="sketch row-center">
      <div v-for="(item, index) in sketch" :key="index" class="sketch-item row">
        <i-icon :icon="item.icon" />
        <span>{{ $t(item.text) }}</span>
      </div>
    </div>

    <h5-upload @handleFileUpload="handleFileUpload" @handleGoogleDriveUpload="handleGoogleDriveUpload" />
  </div>
</template>
<script>
import CoreMixins from '@/mixins/core'
import ImageMixins from '@/mixins/image'

const { SCOPES, CLIENT_ID, API_KEY, APP_ID } = process.env.GOOGLE

let tokenClient
let accessToken = null
let pickerInited = false
let gisInited = false

export default {
  mixins: [CoreMixins, ImageMixins],

  props: {
    accept: {
      type: String,
      default: '*',
    },
    limit: {
      type: Number,
      default: 3,
    },
  },
  data() {
    return {
      sketch: [
        { icon: 'sketch_1', text: 'components.pannel.sketch1' },
        { icon: 'sketch_2', text: 'components.pannel.sketch2' },
        { icon: 'sketch_3', text: 'components.pannel.sketch3' },
      ],
    }
  },
  mounted() {
    if (!pickerInited) {
      this.loadJS('https://apis.google.com/js/api.js').then(() => {
        this.gapiLoaded()
      })
    } else {
      pickerInited = true
    }

    if (!gisInited) {
      this.loadJS('https://accounts.google.com/gsi/client').then(() => {
        this.gisLoaded()
      })
    } else {
      gisInited = true
    }
  },
  methods: {
    handleUpload() {
      this.$refs.uploadInput.click()
    },
    onFileInputChange(e) {
      this.$emit('change', e)
    },
    handleFileUpload() {
      this.handleUpload()
      window.scrollTo({
        top: 0,
        behavior: 'smooth',
      })
    },
    async handleGoogleDriveUpload() {
      // JS 资源不存在,再次 fetch
      if (!pickerInited) {
        await this.loadJS('https://apis.google.com/js/api.js')
        this.gapiLoaded()
      }

      if (!gisInited) {
        await this.loadJS('https://accounts.google.com/gsi/client')
        this.gisLoaded()
      }

      if (accessToken !== null) {
        await this.createPicker()
        return
      }

      this.handleAuthClick()
      if (this.$store.state.isH5) {
        window.scrollTo({
          top: 0,
          behavior: 'smooth',
        })
      }
    },

    /**
     * Callback after api.js is loaded.
     */
    gapiLoaded() {
      gapi.load('client:picker', this.initializePicker)
    },

    /**
     * Callback after the API client is loaded. Loads the
     * discovery doc to initialize the API.
     */
    async initializePicker() {
      await gapi.client.load('https://www.googleapis.com/discovery/v1/apis/drive/v3/rest')
      pickerInited = true
    },

    /**
     * Callback after Google Identity Services are loaded.
     */
    gisLoaded() {
      tokenClient = google.accounts.oauth2.initTokenClient({
        client_id: CLIENT_ID,
        scope: SCOPES,
        callback: '',
      })
      gisInited = true
    },

    /**
     *  Sign in the user upon button click.
     */
    handleAuthClick() {
      tokenClient.callback = async response => {
        if (response.error !== undefined) {
          throw response
        }
        accessToken = response.access_token
        await this.createPicker()
      }

      if (accessToken === null) {
        // Prompt the user to select a Google Account and ask for consent to share their data
        // when establishing a new session.
        tokenClient.requestAccessToken({ prompt: 'consent' })
      } else {
        // Skip display of account chooser and consent dialog for an existing session.
        tokenClient.requestAccessToken({ prompt: '' })
      }
    },

    /**
     *  Sign out the user upon button click.
     */
    handleSignoutClick() {
      if (accessToken) {
        accessToken = null
        google.accounts.oauth2.revoke(accessToken)
      }
    },

    /**
     *  Create and render a Picker object for searching images.
     */
    createPicker() {
      const view = new google.picker.DocsView(google.picker.ViewId.DOCS)
      // 只读取 docx、xlsx、pptx、png、jpeg、jpg 类型文件
      view.setMimeTypes(
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.google-apps.document,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.google-apps.spreadsheet,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/vnd.google-apps.presentation,application/pdf,image/png,image/jpeg,image/jpg'
      )
      view.setIncludeFolders(true)

      const shareview = new google.picker.DocsView(google.picker.ViewId.DOCS)
      // 只读取 docx、xlsx、pptx、png、jpeg、jpg 类型文件
      shareview.setMimeTypes(
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.google-apps.document,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.google-apps.spreadsheet,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/vnd.google-apps.presentation,application/pdf,image/png,image/jpeg,image/jpg'
      )
      shareview.setEnableDrives(true)
      shareview.setIncludeFolders(true)
      const picker = new google.picker.PickerBuilder()
        // .enableFeature(google.picker.Feature.NAV_HIDDEN)
        .enableFeature(google.picker.Feature.MULTISELECT_ENABLED)
        // .setDeveloperKey(API_KEY)
        .setAppId(APP_ID)
        .setOAuthToken(accessToken)
        .addView(view)
        .addView(shareview)
        // .addView(new google.picker.DocsUploadView())
        .setCallback(this.pickerCallback)
        .build()
      picker.setVisible(true)
    },

    /**
     * Displays the file details of the user's selection.
     * @param {object} data - Containers the user selection from the picker
     */
    async pickerCallback(data) {
      if (data.action === google.picker.Action.PICKED) {
        const document = data[google.picker.Response.DOCUMENTS][0]
        const fileId = document[google.picker.Document.ID]
        const res = await gapi.client.drive.files.get({
          fileId,
          fields: '*',
        })
        const { name, mimeType, exportLinks, webContentLink } = res.result

        const res2 = await gapi.client.drive.files.download({
          fileId,
        })
        const downloadUri = res2.result.response.downloadUri

        console.log(fileId)
        console.log(res.result)
        console.log(name, mimeType, exportLinks)
        console.log(downloadUri)

        // let fetchUrl = webContentLink
        // let fetchUrl = `https://www.googleapis.com/drive/v3/files/${fileId}?alt=media`
        let fetchUrl = downloadUri
        let fileName = name
        let fileType = mimeType
        // 如果 mimeType 是如下类型,说明是 google drive 内建类型,需要转换格式
        if (mimeType === 'application/vnd.google-apps.document') {
          fetchUrl = exportLinks['application/vnd.openxmlformats-officedocument.wordprocessingml.document']
          fileName = `${name}.docx`
          fileType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        } else if (mimeType === 'application/vnd.google-apps.spreadsheet') {
          fetchUrl = exportLinks['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
          fileName = `${name}.xlsx`
          fileType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        } else if (mimeType === 'application/vnd.google-apps.presentation') {
          fetchUrl = exportLinks['application/vnd.openxmlformats-officedocument.presentationml.presentation']
          fileName = `${name}.pptx`
          fileType = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
        }

        this.startLoading()

        fetch(fetchUrl, {
          method: 'GET',
          headers: {
            Authorization: `Bearer ${accessToken}`,
          },
        })
          .then(response => response.blob()) // 将响应转为 Blob
          .then(blob => {
            // 创建 File 对象
            const file = new File([blob], fileName, { type: fileType })
            console.log(file)

            this.stopLoading()

            this.$emit('change', [file], 'google_drive')
          })
          .catch(error => {
            this.stopLoading()
            console.error('Error downloading file:', error)
          })
      }
    },
  },
}
</script>

<style lang="scss" scoped>
#hide-input {
  display: none;
}
@media (min-width: 980px) {
  .pannel-wrap {
    width: 680px;
    margin: 0 auto;
  }
  .upload-box {
    padding: 20px;
    border-radius: 20px;
    padding: 16px;
    background: var(--primary);
    cursor: pointer;
    &:hover {
      opacity: 0.9;
    }
  }
  .upload-content {
    height: 320px;
    border-radius: 10px;
    border: 2px dashed rgba(255, 255, 255, 0.5);
    .upload-icon {
      width: 72px;
      height: 72px;
    }
    .desc {
      min-width: 246px;
      padding: 14px 16px;
      background: #fff;
      margin-top: 18px;
      font-family: Manrope-SemiBold, Manrope;
      font-weight: 600;
      font-size: 16px;
      color: #000;
      line-height: 20px;
      text-align: center;
      border-radius: 4px;
    }
    .or {
      font-family: Manrope-Regular, Manrope;
      font-weight: 400;
      font-size: 13px;
      color: #ffffff;
      line-height: 22px;
      margin: 4px 0;
      &::before,
      &::after {
        content: '';
        width: 59px;
        height: 1px;
        background: #fff;
        opacity: 0.24;
      }
      &::before {
        margin-right: 10px;
      }

      &::after {
        margin-left: 10px;
      }
    }
    .google-drive {
      margin-bottom: 42px;
      display: flex;
      align-items: center;
      .icon {
        width: 22px;
        height: 22px;
      }
      .text {
        margin-left: 6px;
        font-family: Manrope-Bold;
        font-weight: bold;
        font-size: 14px;
        color: #ffffff;
        line-height: 22px;
        text-align: left;
        font-style: normal;
        text-decoration-line: underline;
        &:hover {
          opacity: 0.7;
        }
      }
    }
    .tip {
      font-family: Manrope-Regular, Manrope;
      font-weight: 400;
      font-size: 13px;
      color: rgba(255, 255, 255, 0.8);
      line-height: 22px;
      text-align: center;
    }
  }
  .sketch {
    padding: 48px 12px 0;
    gap: 28px;
    &-item {
      max-width: 200px;
      font-family: Manrope-Regular, Manrope;
      font-weight: 400;
      font-size: 18px;
      color: #020322;
      line-height: 18px;
    }
    .i-icon {
      width: 48px;
      height: 48px;
      margin-right: 12px;
      flex-shrink: 0;
    }
  }
}
@media (max-width: 980px) {
  .pannel-wrap {
    width: 6.8rem;
    margin: 0 auto;
  }
  .upload-box {
    padding: 0.2rem;
    background: var(--primary);
    border-radius: 0.24rem;
  }
  .upload-content {
    padding: 0.6rem 0 0.24rem;
    // height: 3.96rem;
    border-radius: 0.12rem;
    border: 0.04rem dashed rgba(255, 255, 255, 0.5);
    .upload-icon {
      width: 1rem;
      height: 1rem;
    }
    .desc {
      min-width: 4.92rem;
      padding: 0.28rem 0.32rem;
      margin-top: 0.3rem;
      background: #fff;
      font-family: Manrope-SemiBold, Manrope;
      font-weight: 600;
      font-size: 0.32rem;
      color: #000000;
      line-height: 0.4rem;
      text-align: center;
      border-radius: 0.08rem;
    }
    .or {
      font-family: Manrope-Regular, Manrope;
      font-weight: 400;
      font-size: 0.26rem;
      color: #ffffff;
      line-height: 0.44rem;
      margin: 0.08rem 0;
      &::before,
      &::after {
        content: '';
        width: 1.18rem;
        height: 0.02rem;
        background: #fff;
        opacity: 0.24;
      }
      &::before {
        margin-right: 0.2rem;
      }

      &::after {
        margin-left: 0.2rem;
      }
    }
    .google-drive {
      margin-bottom: 0.64rem;
      display: flex;
      align-items: center;
      .icon {
        width: 0.4rem;
        height: 0.4rem;
      }
      .text {
        margin-left: 0.1rem;
        font-family: Manrope-Bold;
        font-weight: bold;
        font-size: 0.28rem;
        color: #ffffff;
        line-height: 0.44rem;
        text-align: left;
        font-style: normal;
        text-decoration-line: underline;
      }
    }
    .tip {
      font-family: Manrope-Regular, Manrope;
      font-weight: 400;
      font-size: 0.26rem;
      color: rgba(255, 255, 255, 0.8);
      line-height: 0.3rem;
    }
  }
  .sketch {
    margin-top: 0.48rem;
    gap: 0.28rem;
    &-item {
      max-width: 2.08rem;
      font-family: Manrope-Regular, Manrope;
      font-weight: 400;
      font-size: 0.26rem;
      color: #020322;
      line-height: 0.26rem;
    }
    .i-icon {
      width: 0.72rem;
      height: 0.72rem;
      margin-right: 0.12rem;
      flex-shrink: 0;
    }
  }
}
</style>

    

下课!