侧边栏壁纸
  • 累计撰写 4 篇文章
  • 累计创建 17 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

使用mclo.gs的API写个适合中国宝宝体质的日志分享网站

ideafox
2024-08-09 / 0 评论 / 0 点赞 / 105 阅读 / 0 字

前言

mclo.gs 是一个用于上传Minecraft日志的网站,并可以提供一定程度的自动分析。但总有人说看不懂英语,于是我利用了它的API写一个适合中国宝宝体质的日志分享网站。

API分析

mclo.gs的粘贴日志API使用post请求,请求体格式为application/x-www-form-urlencoded,请求字段为content,最大长度为10MiB25k行。

代码示例

纯静态+bootstrap:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>日志上传,基于mclo.gs!</title>
    <link rel="stylesheet" href="./upload/bootstrap.css" />
    <style>
			body {
				display: flex;
				flex-direction: column;
				min-height: 100vh;
				margin: 0;
				/* Remove default margin */
				background-color: #f0f0f0;
				/* 浅灰色背景 */
				padding: 1rem;
				/* 给 body 添加一些内外边距 */
			}

			.container {
				flex: 1;
				padding-top: 2rem;
				/* Add some top padding */
				padding-bottom: 2rem;
				/* Add some bottom padding */
				background-color: #fff;
				/* 白色背景 */
				border-radius: 10px;
				/* 圆角 */
				box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
				/* 可选:添加阴影效果 */
				max-width: 800px;
				/* 限制最大宽度 */
				width: 100%;
				/* 确保在小屏幕上占满宽度 */
				box-sizing: border-box;
				/* 确保内边距不会增加元素的总宽度 */
				margin: 0 auto;
				/* 水平居中 */
				margin-bottom: 1rem;
				/* 与页脚之间的边距 */
			}

			footer {
				flex-shrink: 0;
				padding: 1rem 0;
				text-align: center;
				width: 100%;
				/* 确保 footer 占满整个宽度 */
				margin: 0 auto;
				/* 水平居中 */
				max-width: 800px;
				/* 与 .container 保持一致的最大宽度 */
				margin-top: 1rem;
				/* 与 .container 之间的边距 */
			}
		</style>
	</head>
	<body>
		<div class="container">
			<h1 class="text-center mt-5 mb-4">将你的日志上传到mclo.gs,分享给别人!</h1>
			<div class="row justify-content-center">
				<div class="col-12 col-md-8">
					<label for="fileInput">将日志粘贴到此</label>
					<div class="form-group mb-4">
						<textarea id="logContent" class="form-control" rows="10" placeholder="将日志粘贴到此!"></textarea>
					</div>
					<button id="uploadButton" class="btn btn-primary w-100 mb-4" onclick="uploadLog()">上传粘贴内容</button>
					<div class="mt-3 mb-4">
						<label for="fileInput">或上传文件</label>
						<div class="input-group mb-3">
							<input type="file" class="form-control" id="fileInput"
								accept=".txt,.log,application/octet-stream">
						</div>
						<button id="fileUploadButton" class="btn btn-primary w-100 mb-4"
							onclick="uploadFile()">上传文件</button>
					</div>
					<div class="mt-3 mb-4">
						<label for="responseOutput">返回结果:</label>
						<pre id="responseOutput" class="bg-light p-3 mb-4"></pre>
						<button id="copyButton" class="btn btn-secondary w-100"
							onclick="copyToClipboard()">点击复制,分享给他人!</button>
					</div>
				</div>
			</div>
		</div>

		<footer>
			<div class="container">
				<p>黑ICP备2024025211号-1</p>
			</div>
		</footer>

		<script>
			let requestCount = 0;
			let firstRequestTime = null;

			function uploadLog() {
				const logContent = document.getElementById('logContent').value;
				const formData = new FormData();
				formData.append('content', logContent);

				const uploadButton = document.getElementById('uploadButton');
				const copyButton = document.getElementById('copyButton');

				const currentTime = new Date().getTime();

				if (firstRequestTime === null) {
					firstRequestTime = currentTime;
				}

				if (currentTime - firstRequestTime < 60000) {
					if (requestCount >= 5) {
						alert('请求频率过高,请稍后再试。');
						return;
					}
				} else {
					firstRequestTime = currentTime;
					requestCount = 0;
				}

				requestCount++;

				if (!logContent.trim()) {
					alert('上传失败,请粘贴日志!');
					return;
				}

				uploadButton.innerHTML = `
                <button class="btn btn-primary" type="button" disabled>
                    <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
                    <span class="visually-hidden">Loading...</span>
                </button>
            `;

				fetch('https://api.mclo.gs/1/log', {
						method: 'POST',
						body: formData
					})
					.then(response => response.json())
					.then(data => {
						document.getElementById('responseOutput').textContent = data.url ? data.url : JSON.stringify(data,
							null, 2);
					})
					.catch(error => {
						console.error('Error:', error);
						document.getElementById('responseOutput').textContent = JSON.stringify(error, null, 2);
					})
					.finally(() => {
						uploadButton.innerHTML = '上传';
						copyButton.classList.remove('btn-success');
						copyButton.classList.add('btn-secondary');
						copyButton.textContent = '复制链接';
					});
			}

			function copyToClipboard() {
				const responseOutput = document.getElementById('responseOutput');
				const text = responseOutput.textContent;
				const copyButton = document.getElementById('copyButton');

				if (!text || text.includes('Error')) {
					copyButton.classList.remove('btn-secondary');
					copyButton.classList.add('btn-danger');
					copyButton.textContent = '复制失败,请确认上传成功后再点此按钮!';
					return;
				}

				const tempInput = document.createElement('textarea');
				tempInput.value = text;
				document.body.appendChild(tempInput);
				tempInput.select();
				document.execCommand('copy');
				document.body.removeChild(tempInput);

				copyButton.classList.remove('btn-secondary');
				copyButton.classList.add('btn-success');
				copyButton.textContent = '复制成功!';
			}

			function uploadFile() {
				const fileInput = document.getElementById('fileInput');
				const file = fileInput.files[0];
				if (!file) {
					alert('请选择要上传的文件!');
					return;
				}

				const reader = new FileReader();
				reader.onload = function(event) {
					const logContent = event.target.result;
					const formData = new FormData();
					formData.append('content', logContent);

					const fileUploadButton = document.getElementById('fileUploadButton');
					const copyButton = document.getElementById('copyButton');

					fileUploadButton.innerHTML = `
			                <button class="btn btn-primary" type="button" disabled>
			                    <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
			                    <span class="visually-hidden">Loading...</span>
			                </button>
			            `;

					fetch('https://api.mclo.gs/1/log', {
							method: 'POST',
							body: formData
						})
						.then(response => response.json())
						.then(data => {
							document.getElementById('responseOutput').textContent = data.url ? data.url : JSON.stringify(
								data, null, 2);
						})
						.catch(error => {
							console.error('Error:', error);
							document.getElementById('responseOutput').textContent = JSON.stringify(error, null, 2);
						})
						.finally(() => {
							fileUploadButton.innerHTML = '上传文件';
							copyButton.classList.remove('btn-success');
							copyButton.classList.add('btn-secondary');
							copyButton.textContent = '复制链接';
						});
				};
				reader.readAsText(file); // 将文件读取为文本
			}
		</script>
	</body>
</html>

该代码使用bootstrap进行美化,其中的引入部分可自行下载文件到本地或使用CDN引入。同时该代码对粘贴内容做出一定判断,如果为空,则提示需要粘贴内容,并提供一个复制链接的功能。

vue+ElementPlus:

<template>
  <div id="app">
    <el-container style="min-height: 100vh; background-color: #f0f0f0; padding: 1rem;">
  <el-card class="header-card" shadow="always">
    <el-row justify="space-between" align="middle">
      <el-col :span="12">
        <h1 style="color: #409EFF; margin: 0">将你的日志上传到mclo.gs,分享给别人!</h1>
      </el-col>
      <el-col :span="12" style="text-align: right">
        <el-button type="primary" link @click="openBlog">
          返回博客主页
        </el-button>
      </el-col>
    </el-row>
  </el-card>
      <el-main>
        <h1 class="text-center"></h1>
        <el-card class="container" shadow="always">
          <el-steps :active="activeStep" align-center>
            <el-step title="选择上传方式" :icon="Edit" />
            <el-step title="上传日志" />
            <el-step title="获取链接" />
          </el-steps>
          <div v-if="activeStep === 0" class="step-content">
            <el-radio-group v-model="uploadMethod" size="large">
              <el-radio-button label="paste" border>粘贴日志</el-radio-button>
              <el-radio-button label="file" border>上传文件</el-radio-button>
            </el-radio-group>
            
            <div class="button-group">
              <el-button type="primary" @click="nextStep" :disabled="!uploadMethod">下一步</el-button>
            </div>
          </div>
          <div v-else-if="activeStep === 1" class="step-content">
            <div v-if="uploadMethod === 'paste'">
              <el-input
                type="textarea"
                :rows="10"
                placeholder="请将日志粘贴在此处!"
                v-model="logContent">
              </el-input>
            </div>
            <div v-else-if="uploadMethod === 'file'">
              <el-upload
                action="#"
                :auto-upload="false"
                :on-change="handleFileChange"
                accept=".txt,.log">
                <el-button type="primary" >点击上传文件</el-button>
              </el-upload>
            </div>
            <div class="button-group">
              <el-button type="primary" @click="uploadLogOrFile" v-loading.fullscreen.lock="fullscreenLoading">上传</el-button>
              <el-button type="default" @click="prevStep">上一步</el-button>
            </div>
          </div>
          <div v-else class="step-content">
            <el-result
              icon="success"
              title="成功上传"
              :sub-title="`链接为:${responseOutput},您可通过复制链接按钮进行复制,分享给他人。`"
            >
              <template #extra>
                <el-button type="primary" @click="ResetStep">重新上传</el-button>
                <el-button type="secondary" @click="copyToClipboard">{{ copyButtonText }}</el-button>
              </template>
            </el-result>
          </div>
        </el-card>
      </el-main>
      <el-footer style="display: flex; justify-content: center; align-items: center;">
        <el-card style="width: 100%; max-width: 800px; text-align: center;">
          <p>黑ICP备2024025211号-1</p>
        </el-card>
      </el-footer>
    </el-container>
  </div>
</template>

<script>
import { ref } from 'vue';
import { ElMessage,ElLoading } from 'element-plus';

export default {
  

  setup() {
    const fullscreenLoading = ref(false);
    const activeStep = ref(0);
    const uploadMethod = ref('');
    const logContent = ref('');
    const fileContent = ref('');
    const responseOutput = ref('');
    const copyButtonText = ref('复制链接');
    const openBlog = () => {
  window.open('https://ideafox.top')
};
    
    const nextStep = () => {
      if (!uploadMethod.value) {
        ElMessage.error('请选择一种上传方式!');
        return;
      }
      activeStep.value++;
    };

    const prevStep = () => {
      activeStep.value--;
    };
    const ResetStep = ()=>{
      activeStep.value = 0;
      uploadMethod.value = '';
      logContent.value = '';
      fileContent.value = '';
      responseOutput.value = '';
    };

    const handleFileChange = (file, fileList) => {
      const allowedExtensions = ['txt', 'log'];
  const fileName = file.raw.name;
  const extension = fileName.split('.').pop().toLowerCase();
  
  if (!allowedExtensions.includes(extension)) {
    ElMessage.error('仅支持上传.txt和.log类型的文件');
    return; // 阻止后续文件读取
  }
      const reader = new FileReader();
      reader.onload = e => fileContent.value = e.target.result;
      reader.readAsText(file.raw);
    };

    const uploadLogOrFile = async () => {
      fullscreenLoading.value = true
      if ((uploadMethod.value === 'paste' && !logContent.value.trim()) || 
          (uploadMethod.value === 'file' && !fileContent.value)) {
        ElMessage.error(uploadMethod.value === 'paste' ? '请先粘贴或输入日志内容!' : '请选择一个文件进行上传!');
        fullscreenLoading.value = false;
        return;
      }

      const formData = new FormData();
      let content = uploadMethod.value === 'paste' ? logContent.value : fileContent.value;

      formData.append('content', content);

      try {
        const res = await fetch('https://api.mclo.gs/1/log', {
          method: 'POST',
          body: formData,
        });
        const data = await res.json();

        if(data.success) {
          responseOutput.value = data.url;
          activeStep.value++;
        } else {
          ElMessage.error(`上传失败:${data.error}`);
        }
      } catch(error) {
        ElMessage.error('请求过程中出现错误,请稍后再试。');
      } finally {
        fullscreenLoading.value = false;
      }
    };

    const copyToClipboard = () => {
      if (!responseOutput.value) {
        ElMessage.error('没有可复制的链接!');
        return;
      }

      navigator.clipboard.writeText(responseOutput.value).then(() => {
        ElMessage.success('链接已复制到剪贴板!');
      }).catch(err => {
        ElMessage.error('无法复制链接,请手动复制。');
      });
    };

    return {
      activeStep,
      uploadMethod,
      logContent,
      responseOutput,
      copyButtonText,
      nextStep,
      prevStep,
      handleFileChange,
      uploadLogOrFile,
      copyToClipboard,
      ResetStep,
      fullscreenLoading,
      openBlog,
    };
  }
}
</script>

<style scoped>
.container {
  max-width: 800px;
  margin: 0 auto;
  padding: 2rem;
  border-radius: 10px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  background-color: #fff;
}

.text-center {
  text-align: center;
}

.el-steps {
  margin: 20px 0;
}

.button-group {
  display: flex;
  justify-content: space-between;
  margin-top: 20px;
}

.step-content {
  text-align: center;
}
.el-menu--horizontal > .el-menu-item:nth-child(1) {
  margin-right: auto;
}
</style>

需导入 Element-Plus ,可直接粘贴到APP.vue文件,并通过 npm run build 命令导出为纯静态页面。

总结

虽然该代码比较简陋,但足以应对大部分需求,同时也更适合中国宝宝!

0
博主关闭了所有页面的评论