likes
comments
collection
share

接口返回html,前端如何实现预览并下载成多种格式?

作者站长头像
站长
· 阅读数 50

需求简介

昨天遇到一个很小众的需求:点击一个按钮后,请求一个接口,接口返回html,需要实现这个html的预览与下载。 接口返回html,前端如何实现预览并下载成多种格式?

这个功能其实很简单,无非就是展示一个html和纯前端实现一个文件的下载。想着有些人可能刚入门,为了不让他们去某N搜索,就简单分享一下自己的实现方案吧!

技术方案分析

接口如果直接返回一个html说明这个接口本身就是一个后端实现的网页,可以直接在浏览器访问的。

那么实现这个接口返回的html预览就有两个很简单的办法:

  • 使用iframe直接展示
  • 请求接口,将其返回的html渲染:vue直接使用v-html渲染或者react使用dangerouslySetInnerHTML渲染!

下载的话,不同格式有不同方案:

下载html源文件,可以直接创建一个a标签;下载为图片的话,可以使用htmlCanvas;下载为pdf的话,可以使用htmlCanvas + pdfjs

实现html预览

假设这个接口名为http://127.0.0.1:5500/src/pages/flex/IterableEvents/

iframe方式

使用iframe是最简单的一种预览方式,而且基本不存在什么样式或者跨域问题!

react

const Component = () => {
  const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/"
  return (
    <div style={{ height: '600px', padding: '20px 0' }}>
      <iframe style={{ width: '100%', height: '100%' }} src={url}></iframe>}
    </div>
  )
}

vue2

<template>
  <div :style="{ height: '600px', padding: '20px 0' }">
    <iframe :style="{ width: '100%', height: '100%' }" :src="url"></iframe>
  </div>
</template>

<script>
export default {
  data() {
    return {
      url: 'http://127.0.0.1:5500/src/pages/flex/IterableEvents/'
    };
  }
};
</script>

接口返回html,前端如何实现预览并下载成多种格式?

请求接口,数据渲染方式

react写法

首先,我们进行数据请求

const Component = () => {
  const [htmlContent, setHtmlContent] = useState('')
  const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/"

  useEffect(() => {
    fetch(url)
      .then((response) => response.text())
      .then((data) => {
        // data就是接口返回的数据
        console.log('data: ', data);
        setHtmlContent(data)
      })
      .catch((error) => {
        console.error('Error fetching HTML:', error)
      })
  }, [])

  // 渲染html
  return ()
}

上述代码中,我们使用fetch对接口进行了请求,其返回数据就是一个html文本

接口返回html,前端如何实现预览并下载成多种格式?

返回的文本我们储存在了htmlContent中,现在,我们使用react的dangerouslySetInnerHTML进行渲染。

在React中,dangerouslySetInnerHTML用于直接设置HTML内容,它允许你将一个HTML字符串插入到DOM中。这通常是为了渲染从服务器接收到的HTML内容,或者是需要插入动态生成的HTML字符串。

const Component = () => {
  const [htmlContent, setHtmlContent] = useState('')
  const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/"

  useEffect(() => {
    fetch(url)
      .then((response) => response.text())
      .then((data) => {
        // data就是接口返回的数据
        console.log('data: ', data);
        setHtmlContent(data)
      })
      .catch((error) => {
        console.error('Error fetching HTML:', error)
      })
  }, [])
  
  return (
    <div style={{ width: '100%', height: '100%', overflow: 'auto' }}>
      <div dangerouslySetInnerHTML={{ __html: htmlContent }} style={{ width: '100%' }}></div>
    </div>
  )
}

vue写法

vue的实现过程和react是几乎一致的

<template>
  <div :style="{ width: '100%', height: '100%', overflow: 'auto' }">
    <div v-html="htmlContent" :style="{ width: '100%' }" ></div>
  </div>
</template>

<script>
export default {
  name: 'MyComponent',
  data() {
    return {
      htmlContent: ''
    };
  },
  created() {
    const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/";
    fetch(url)
      .then(response => response.text())
      .then(data => {
        console.log('data: ', data);
        this.htmlContent = data;
      })
      .catch(error => {
        console.error('Error fetching HTML:', error);
      });
  }
};
</script>

这种方式渲染出的页面和iframe几乎是一致的:

接口返回html,前端如何实现预览并下载成多种格式?

但要注意,接口不能跨域,跨域的话,必然是渲染不出来的!

实现下载

下载成html源文件

const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/"

// 下载
const downLoad = async () => {
    try {
        const link = document.createElement('a')
        link.href = url
        link.click()
    } catch (error) {
        message.error('Download failed')
    }
}

这种方式非常简单,其实就是借助a标签实现了文件下载,但注意url不能跨域!

下载成图片

注意:下载的前提是接口不跨域

下载成图片可以借助htmlcanvas实现截图下载功能

首先需要安装这个插件

npm i html2canvas

iframe形式

import html2canvas from 'html2canvas'
const Component = () => {
  const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/"

  // 下载
  const downLoad = async () => {
      try {
          const iframe = document.getElementById('myPage')
          targetHtml = iframe?.contentDocument || iframe?.contentWindow?.document
          // scale可以调整图片清晰度
          html2canvas(targetHtml, { scale: 1, useCORS: true })
            .then((canvas) => {
                  const imgData = canvas.toDataURL('image/png');
                  const link = document.createElement('a');
                  link.href = imgData;
                  link.download = 'XXXXX.png';
                  link.click();
            })
      } catch (error) {
          message.error('Download failed')
      }
  }
  
  return (
    <div style={{ height: '600px', padding: '20px 0' }}>
      <iframe style={{ width: '100%', height: '100%' }} src={url} id='myPage'></iframe>}
    </div>
  )
}

vue写法和这个几乎一样,把downLoad方法照抄就行!

请求渲染的方式

和iframe的写法完全一致,改一下targetHtml的获取就行!

import html2canvas from 'html2canvas'
const Component = () => {
  const [htmlContent, setHtmlContent] = useState('')
  const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/"

    // 下载
  const downLoad = async () => {
      try {
          const targetHtml = document.getElementById('myPage')
          // scale可以调整图片清晰度
          html2canvas(targetHtml, { scale: 1, useCORS: true })
            .then((canvas) => {
                  const imgData = canvas.toDataURL('image/png');
                  const link = document.createElement('a');
                  link.href = imgData;
                  link.download = 'XXXXX.png';
                  link.click();
            })
      } catch (error) {
          message.error('Download failed')
      }
  }
  
  useEffect(() => {
    fetch(url)
      .then((response) => response.text())
      .then((data) => {
        setHtmlContent(data)
      })
      .catch((error) => {
        console.error('Error fetching HTML:', error)
      })
  }, [])
  
  return (
    <div style={{ width: '100%', height: '100%', overflow: 'auto' }}>
      <div dangerouslySetInnerHTML={{ __html: htmlContent }} style={{ width: '100%' }} id='myPage'></div>
    </div>
  )
}

下载成pdf

注意:下载的前提是接口不跨域

react写法

下载成pdf,首先我们需要先下载成图片,然后借助pdfjs实现。我们需要安装html2canvas和pdfjs两个插件

npm i html2canvas
npm i pdfjs
import html2canvas from 'html2canvas'
import jsPDF from 'jspdf
  
const Component = () => {
  const [htmlContent, setHtmlContent] = useState('')
  const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/"

    // 下载
  const downLoad = async () => {
      try {
          const targetHtml = document.getElementById('myPage')
          const doc = new jsPDF();
          html2canvas(targetHtml, { scale: 1, useCORS: true })
            .then((canvas) => {
              const imgData = canvas.toDataURL('image/png');
              const imgWidth = 210; 
              const pageHeight = 295; 
              const imgHeight = canvas.height * imgWidth / canvas.width;
              let heightLeft = imgHeight;
              let position = 0;
              doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
              heightLeft -= pageHeight;
              while (heightLeft >= 0) {
                  position = heightLeft - imgHeight;
                  doc.addPage();
                  doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
                  heightLeft -= pageHeight;
              }
              doc.save('webpage.pdf')
              message.destroy()
              setConfirmLoading(false)
            })
            .catch((error) => {
              
            })
      } catch (error) {
          message.error('Download failed')
      }
  }
  
  useEffect(() => {
    fetch(url)
      .then((response) => response.text())
      .then((data) => {
        setHtmlContent(data)
      })
      .catch((error) => {
        console.error('Error fetching HTML:', error)
      })
  }, [])
  
  return (
    <div style={{ width: '100%', height: '100%', overflow: 'auto' }}>
      <div dangerouslySetInnerHTML={{ __html: htmlContent }} style={{ width: '100%' }} id='myPage'></div>
    </div>
  )
}

图片下载成都是pdf都是社区成熟的方案,代码懂了,可以直接百度!

vue写法

<template>
  <div :style="{ width: '100%', height: '100%', overflow: 'auto' }">
    <div v-html="htmlContent" :style="{ width: '100%' }" ref="myPage"></div>
    <button @click="downLoad">Download as PDF</button>
  </div>
</template>

<script>
import { ref, onMounted } from 'vue';
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';

export default {
  name: 'MyComponent',
  setup() {
    const htmlContent = ref('');
    const myPage = ref(null);
    const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/";

    const downLoad = async () => {
      try {
        const targetHtml = myPage.value;
        const doc = new jsPDF();
        const canvas = await html2canvas(targetHtml, { scale: 1, useCORS: true });
        const imgData = canvas.toDataURL('image/png');
        const imgWidth = 210; // A4 paper width in mm
        const pageHeight = 295; // A4 paper height in mm
        const imgHeight = canvas.height * imgWidth / canvas.width;
        let heightLeft = imgHeight;
        let position = 0;

        doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
        heightLeft -= pageHeight;

        while (heightLeft >= 0) {
          position = heightLeft - imgHeight;
          doc.addPage();
          doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
          heightLeft -= pageHeight;
        }

        doc.save('webpage.pdf');
      } catch (error) {
        console.error('Download failed:', error);
      }
    };

    onMounted(() => {
      fetch(url)
        .then(response => response.text())
        .then(data => {
          htmlContent.value = data;
        })
        .catch(error => {
          console.error('Error fetching HTML:', error);
        });
    });

    return {
      htmlContent,
      myPage,
      downLoad
    };
  }
};
</script>

总结

这篇文章分享了如何将一个接口返回的html进行渲染并下载,方案都很朴实无华,最简单的还是iframe的方式!想相信能对各种有一点帮助!感谢大家的关注!

转载自:https://juejin.cn/post/7369868541934845967
评论
请登录