步骤一

此处申请免费的KEY

如 :b9b42bbb-e5a5-4e55-a71d-xxxxxxxxx

步骤二

这是他的API文档

我们用的是java,所以就得用okhttp和阿里的fastjson

第三步

导入Maven依赖

<dependency>
    <groupId>com.alibaba.fastjson2</groupId>
    <artifactId>fastjson2</artifactId>
    <version>2.0.40</version>
</dependency>
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.9.1</version>
</dependency>

第四步

编写请求工具类

**
 * @author 木池
 */
public class Draw_StableDrawUtil {

    private static final String API_KEY = "自己申请的key";
    private static final String HEAD_TYPE = "Content-Type";
    private static final String HEAD_ACCEPT = "accept";
    private static final String HEAD_VALUE = "application/json";
    private static final String HEADER_X_PRODIA_KEY = "X-Prodia-Key";
    private static final String MODELS_URL = "https://api.prodia.com/v1/sdxl/generate";
    private static final Integer TIMEOUT_MAX = 5;
    private static final String BASE_URL = "https://api.prodia.com/v1/job/%s";
    private static final long POLL_INTERVAL_MS = 3000;


    private static final OkHttpClient CLIENT = new OkHttpClient.Builder()
            .callTimeout(TIMEOUT_MAX, TimeUnit.MINUTES)
            .readTimeout(TIMEOUT_MAX, TimeUnit.MINUTES)
            .build();
    private static final Logger log = LoggerFactory.getLogger(Draw_StableDrawUtil.class);



    public static String generateText(String content) throws IOException {
        JSONObject object = JSONObject.parseObject(content);
        String prompt = (String) object.get("prompt");
        try {
            prompt = TranslatorsUtils.textTrans(prompt);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        object.put("prompt", prompt);
        String requestBodyJson = object.toJSONString();
        MediaType mediaType = MediaType.parse(HEAD_VALUE);
        Request request = new Request.Builder()
                .url(MODELS_URL)
                .post(RequestBody.create(mediaType, requestBodyJson))
                .addHeader(HEAD_ACCEPT, HEAD_VALUE)
                .addHeader(HEAD_TYPE, HEAD_VALUE)
                .addHeader(HEADER_X_PRODIA_KEY, API_KEY)
                .build();
        try (Response response = CLIENT.newCall(request).execute()) {
            if (response.isSuccessful() && response.body() != null) {
                JSONObject jsonObject = JSONObject.parseObject(response.body().string());
                String jobId = jsonObject.getString("job");
                return getJobDetails(jobId);
            }
            throw new IOException("Request failed: " + response.code() + " " + response.message());
        } catch (InterruptedException e) {
            return e.getMessage();
        }
    }

    /**
     * 获取任务详情
     *
     * @param jobId 任务ID
     * @return 图片地址
     */
    private static String getJobDetails(String jobId) throws IOException, InterruptedException {
        // 构建请求URL
        String url = String.format(BASE_URL, jobId);
        // 构建请求
        Request request = new Request.Builder()
                .url(url)
                .get()
                .addHeader(HEAD_ACCEPT, HEAD_VALUE)
                .addHeader(HEADER_X_PRODIA_KEY, API_KEY)
                .build();
        boolean isComplete = false;
        String imageUrl = null;
        // 开始轮询
        while (!isComplete) {
            // 执行请求并处理响应
            try (Response response = CLIENT.newCall(request).execute()) {
                if (response.isSuccessful() && response.body() != null) {
                    // 读取响应体内容
                    String res = response.body().string();
                    JSONObject jsonObject = JSONObject.parseObject(res);
                    String status = jsonObject.getString("status");
                    // 检查作业状态
                    if ("succeeded".equals(status)) {
                        isComplete = true;
                        imageUrl = jsonObject.getString("imageUrl");
                    } else if ("failed".equals(status)) {
                        throw new IOException("Job failed: " + jobId);
                    }
                } else {
                    throw new IOException("Request failed: " + response.code() + " " + response.message());
                }
            }
            if (!isComplete) {
                Thread.sleep(POLL_INTERVAL_MS);
            }
        }
        return imageUrl;
    }
}

第五步

配合前端vue使用

前端代码我就直接粘贴了,就不多做解释了

<template>
  <div id="app">
    <div class="chat-container" style="height: 820px; max-width: 1350px">
      <div class="selection">
        <div class="container">
          <div class="settings2" @click="chat"><span style="color: #c05ee7"><b>切换AI对话</b></span></div>
          <div class="settings2" @click="newChat"><span style="color: #2f8099"><b>新版AI对话</b></span></div>
        </div>
        <div class="container">
          <div class="settings2" @click="Dalle3"><span style="color: #69a142"><b>切换D3绘画</b></span></div>
          <div class="settings2" @click="Midj"><span style="color: #c65930"><b>切换MJ绘画</b></span></div>
        </div>

        <h3>模型选择</h3>
        <el-select v-model="buildObject.model" placeholder="请选择模型">
          <el-option
              v-for="item in models"
              :key="item.value"
              :label="item.label"
              :value="item.value">
          </el-option>
        </el-select>

        <h3>样式预设</h3>
        <el-select v-model="buildObject.style_preset" placeholder="请选择预设">
          <el-option
              v-for="item in presets"
              :key="item.value"
              :label="item.label"
              :value="item.value">
            <span style="float: left">{{ item.label }}</span>
            <span style="float: right; color: #8492a6; font-size: 13px">{{ item.value }}</span>
          </el-option>
        </el-select>

        <div class="block">
          <h3>steps 步骤</h3>
          <el-slider v-model="buildObject.steps" style="width: 70%;margin-left: 15%"></el-slider>
        </div>

        <h3>采样器</h3>
        <el-select v-model="buildObject.sampler" placeholder="请选择采样器">
          <el-option
              v-for="item in samplers"
              :key="item.value"
              :label="item.label"
              :value="item.value">
          </el-option>
        </el-select>

        <el-tooltip placement="top">
          <div slot="content">为获得最佳性能,请使用以下分辨率之一<br/>1024x1024、1152x896、1216x832、1344x768、1536x640、640x1536、768x1344、832x1216</div>
          <h3>图像宽度</h3>
        </el-tooltip>
        <el-select v-model="buildObject.width" placeholder="请选择图片宽度">
          <el-option
              v-for="item in widths"
              :key="item.value"
              :label="item.label"
              :value="item.value">
          </el-option>
        </el-select>

        <el-tooltip placement="top">
          <div slot="content">为获得最佳性能,请使用以下分辨率之一<br/>1024x1024、1152x896、1216x832、1344x768、1536x640、640x1536、768x1344、832x1216</div>
          <h3>图像高度</h3>
        </el-tooltip>
        <el-select v-model="buildObject.height" placeholder="请选择图片高度">
          <el-option
              v-for="item in widths"
              :key="item.value"
              :label="item.label"
              :value="item.value">
          </el-option>
        </el-select>




      </div>

      <div class="chat-window">
        <div class="chat-header">
          StableDiffusion绘画
        </div>
        <div class="chat-messages" id="chatMessages">
          <div v-for="message in messages" :key="message.id" class="message-row"
               :class="{ 'user-row': message.is_user, 'system-row': !message.is_user }">
            <!-- 系统消息头像 -->
            <img v-if="!message.is_user" :src="system" class="avatar system-avatar" alt="System Avatar">
            <!-- 消息内容 -->
            <div class="message" :class="{ 'user-message': message.is_user, 'system-message': !message.is_user }">
              <div v-if="message.loading" class="loading-box">
                <div class="loader"></div> <!-- 简单的加载动画 -->
              </div>
              <div v-else-if="Array.isArray(message.content)">
                <div v-for="(url, index) in message.content" :key="index" class="image-box">
<!--                  <el-image-->
<!--                      style="width: 200px; height: 200px; margin: 5px;"-->
<!--                      :src="url"-->
<!--                      fit="cover"-->
<!--                      @click="handleImageClick(url)"-->
<!--                  ></el-image>-->
                  <a :href="url" target="_blank">{{ url }}</a>
                </div>
              </div>
              <div v-else>
                {{ message.content }}
              </div>
            </div>
            <!-- 用户消息头像 -->
            <img v-if="message.is_user" :src="user" class="avatar user-avatar" alt="User Avatar">
          </div>
        </div>
        <div class="chat-input">
          <input type="text" v-model="newMessage" @keydown.enter="sendMessage" placeholder="请输入想要绘画的物体"
                 class="input-message">
          <button @click="sendMessage" class="send-button">发送</button>
        </div>
      </div>
    </div>

    <el-dialog :visible.sync="dialogVisible">
      <el-image :src="dialogImageUrl" style="width: 100%;"></el-image>
    </el-dialog>
  </div>
</template>

<script>

export default {
  data() {
    return {
      system: require('@/assets/system.png'),
      user: require('@/assets/user.png'),
      newMessage: '',
      messages: [],
      nextMessageId: 1,
      loading: false, // 用于跟踪图片加载状态
      imageUrls: [], // 用于存储从服务器响应获得的图片链接
      dialogImageUrl: '', // 当前点击的图片的URL
      dialogVisible: false, // 控制模态框的显示


      models: [{
        value: 'dreamshaperXL10_alpha2.safetensors [c8afe2ef]',
        label: 'dreamshaperXL10_alpha2.safetensors [c8afe2ef]'
      }, {
        value: 'dynavisionXL_0411.safetensors [c39cc051]',
        label: 'dynavisionXL_0411.safetensors [c39cc051]'
      }, {
        value: 'juggernautXL_v45.safetensors [e75f5471]',
        label: 'juggernautXL_v45.safetensors [e75f5471]'
      }, {
        value: 'realismEngineSDXL_v10.safetensors [af771c3f]',
        label: 'realismEngineSDXL_v10.safetensors [af771c3f]'
      }, {
        value: 'sd_xl_base_1.0.safetensors [be9edd61]',
        label: 'sd_xl_base_1.0.safetensors [be9edd61]'
      },],

      presets: [ { value: '3d-model', label: '3D模型' },
        { value: 'analog-film', label: '胶片' },
        { value: 'anime', label: '动漫' },
        { value: 'cinematic', label: '电影化的' },
        { value: 'comic-book', label: '漫画书' },
        { value: 'digital-art', label: '数字艺术' },
        { value: 'enhance', label: '增强' },
        { value: 'fantasy-art', label: '奇幻艺术' },
        { value: 'isometric', label: '等轴测' },
        { value: 'line-art', label: '线描' },
        { value: 'low-poly', label: '低多边形' },
        { value: 'neon-punk', label: '霓虹朋克' },
        { value: 'origami', label: '折纸' },
        { value: 'photographic', label: '摄影的' },
        { value: 'pixel-art', label: '像素艺术' },
        { value: 'texture', label: '纹理' },
        { value: 'craft-clay', label: '手工陶土' }],

      samplers:[
        { value: 'Euler', label: 'Euler' },
        { value: 'Euler_a', label: 'Euler a' },
        { value: 'LMS', label: 'LMS' },
        { value: 'Heun', label: 'Heun' },
        { value: 'DPM2', label: 'DPM2' },
        { value: 'DPM2_a', label: 'DPM2 a' },
        { value: 'DPM2S_a', label: 'DPM++ 2S a' },
        { value: 'DPM2M', label: 'DPM++ 2M' },
        { value: 'DPMSDE', label: 'DPM++ SDE' },
        { value: 'DPM_fast', label: 'DPM fast' },
        { value: 'DPM_adaptive', label: 'DPM adaptive' },
        { value: 'LMS_Karras', label: 'LMS Karras' },
        { value: 'DPM2_Karras', label: 'DPM2 Karras' },
        { value: 'DPM2_a_Karras', label: 'DPM2 a Karras' },
        { value: 'DPM2S_a_Karras', label: 'DPM++ 2S a Karras' },
        { value: 'DPM2M_Karras', label: 'DPM++ 2M Karras' },
        { value: 'DPMSDE_Karras', label: 'DPM++ SDE Karras' }
      ],

      widths:[
        {label:"1024",value:"1024"},
        {label:"1152",value:"1152"},
        {label:"1216",value:"1216"},
        {label:"1344",value:"1344"},
        {label:"1536",value:"1536"},
        {label:"640",value:"640"},
        {label:"768",value:"768"},
        {label:"832",value:"832"},
      ],

      heights:[
        {label:"1024",value:"1024"},
        {label:"896",value:"896"},
        {label:"832",value:"832"},
        {label:"768",value:"768"},
        {label:"640",value:"640"},
        {label:"1536",value:"1536"},
        {label:"1344",value:"1344"},
        {label:"1216",value:"1216"},
      ],

        buildObject: {
          model: "sd_xl_base_1.0.safetensors [be9edd61]",
          prompt: "",
          negative_prompt: "badly drawn",
          style_preset: "analog-film",
          steps: 25,
          cfg_scale: 7,
          seed: -1,
          sampler: "DPM++ 2M Karras",
          width: 1024,
          height: 1024
        }

    };
  },
  created() {
    this.messages.push({
      loading: false,
      is_user: false,
      id: this.nextMessageId++,
      content: '请描述你想要的图片,怕你们搞黄,请自行在浏览器打开',
    });
    this.messages.push({
      loading: false,
      is_user: false,
      id: this.nextMessageId++,
      content: '最好更改所需要的样式预设哦,默认是胶片,胶片图片质量好,但是加载慢。',
    });
  },
  methods: {

    Dalle3(){
      this.$router.push('/DallThree');
    },
    Midj(){
      this.$router.push('/DrawMidjourney');
    },
    chat(){
      this.$router.push('/ChatGPTALL');
    },
    newChat(){
      window.location.href = 'https://gpt.xlike.top/#/';
    },

    handleImageClick(url) {
      this.dialogImageUrl = url; // 设置当前的图片URL
      this.dialogVisible = true; // 显示模态框
    },
    sendMessage() {

      this.buildObject.prompt = this.newMessage;

      if (this.newMessage.trim() !== '') {
        const userMessageContent = this.newMessage.trim();
        this.messages.push({
          id: this.nextMessageId++,
          content: userMessageContent,
          is_user: true
        });
        // 添加一个加载中的消息
        const loadingMessageId = this.nextMessageId++;
        this.messages.push({
          id: loadingMessageId,
          content: '',
          is_user: false,
          loading: true
        });
        // 清空输入框
        this.newMessage = '';
        // 发送POST请求

        let jsonPayload = JSON.stringify(this.buildObject);
        this.$http.post("/draw/stableDraw", {prompt: jsonPayload}).then(response => {
          const imageUrlList = response.data; // 根据您的实际响应结构调整这里的路径
          // 更新消息,替换“加载中”消息为图片消息
          this.messages = this.messages.map(message => {
            if (message.id === loadingMessageId) {
              return {
                ...message,
                content: imageUrlList,
                loading: false
              };
            }
            return message;
          });
        }).catch(error => {
          console.error("There was an error!", error);
          // 处理错误情况,例如移除加载消息,显示错误提示等
        });
      }
    }
  }
};
</script>

<style>
/* ...之前的样式不变... */

/* 修改了消息行的样式 */
.message-row {
  display: flex;
  align-items: center;
  margin-bottom: 10px;
}

.system-row {
  justify-content: flex-start; /* 系统消息靠左 */
}

.user-row {
  justify-content: flex-end; /* 用户消息靠右 */
}

.message {
  max-width: 60%; /* 最大宽度,防止内容过少时过宽 */
  padding: 10px;
  border-radius: 15px;
  margin-bottom: 10px;
}

.user-message {
  background-color: #007bff;
  color: white;
}

.system-message {
  background-color: #f0f0f0;
}

.avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  object-fit: cover;
}

.system-avatar {
  margin-right: 10px;
}

.user-avatar {
  margin-left: 10px;
}

.selection {
  width: 300px;
}
.settings {
  width: 85%;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 50px;
  background-color: #f2f2f2;
  border: 1px solid #dcdcdc;
  border-radius: 10px;
  box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
  padding: 5px;
  font-size: 18px;
  color: #333;
  text-shadow: 0 1px 0 #fff;
  margin-top: 20px;
  margin-left: 6%;
  cursor: pointer;
}
.container {
  display: flex;
  justify-content: center; /* 水平居中 */
  align-items: center; /* 垂直居中 */
}
.settings2 {
  width: 39%;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 50px;
  background-color: #f2f2f2;
  border: 1px solid #dcdcdc;
  border-radius: 10px;
  box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
  padding: 5px;
  font-size: 18px;
  color: #333;
  text-shadow: 0 1px 0 #fff;
  margin-top: 20px;
  margin-left: 3%;
  cursor: pointer;
}

</style>