以前,我问过 ActionView::Template::Error (undefined method `image_url')
然后,我解决了错误但没有出现图像,似乎是无法识别照片属性对象或无法在某些库(如 uppy、crooperjs)中获取图像路径,请有人帮帮我
应用程序.js:
//= require popper
//= require bootstrap-sprockets
//= require_tree .
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require("jquery")
文件上传.js:
import 'uppy/dist/uppy.min.css'
import cropbox from 'cropbox'
import {
Core,
FileInput,
Informer,
ProgressBar,
ThumbnailGenerator,
Dashboard,
XHRUpload,
AwsS3,
AwsS3Multipart,
} from 'uppy'
const singleFileUpload = (fileInput) => {
const imagePreview = document.getElementById(fileInput.dataset.previewElement)
const formGroup = fileInput.parentNode
formGroup.removeChild(fileInput)
const uppy = fileUpload(fileInput)
uppy
.use(FileInput, {
target: formGroup,
locale: { strings: { chooseFiles: 'Choose file' } },
})
.use(Informer, {
target: formGroup,
})
.use(ProgressBar, {
target: imagePreview.parentNode,
})
.use(ThumbnailGenerator, {
thumbnailWidth: 600,
})
uppy.on('upload-success', (file, response) => {
// retrieve uploaded file data
const uploadedFileData = response.body['data']
// set hidden field value to the uploaded file data so that it's submitted
// with the form as the attachment
hiddenInput.value = JSON.stringify(uploadedFileData)
cropbox(imagePreview, response.uploadURL, {
onCrop(detail) {
let fileData = JSON.parse(hiddenInput.value)
fileData['metadata']['crop'] = detail
hiddenInput.value = JSON.stringify(fileData)
}
})
})
uppy.on('thumbnail:generated', (file, preview) => {
imagePreview.src = preview
})
}
const multipleFileUpload = (fileInput) => {
const formGroup = fileInput.parentNode
const uppy = fileUpload(fileInput)
uppy
.use(Dashboard, {
target: formGroup,
inline: true,
height: 300,
replaceTargetContent: true,
})
uppy.on('upload-success', (file, response) => {
const hiddenField = document.createElement('input')
hiddenField.type = 'hidden'
hiddenField.name = `album[photos_attributes][${randomstring.generate()}][image]`
hiddenField.value = uploadedFileData(file, response, fileInput)
document.querySelector('form').appendChild(hiddenField)
})
}
const fileUpload = (fileInput) => {
const uppy = Core({
id: fileInput.id,
autoProceed: true,
restrictions: {
allowedFileTypes: fileInput.accept.split(','),
},
})
if (fileInput.dataset.uploadServer == 's3') {
uppy.use(AwsS3, {
companionUrl: '/', // will call Shrine's presign endpoint mounted on `/s3/params`
})
} else if (fileInput.dataset.uploadServer == 's3_multipart') {
uppy.use(AwsS3Multipart, {
companionUrl: '/' // will call uppy-s3_multipart endpoint mounted on `/s3/multipart`
})
} else {
uppy.use(XHRUpload, {
endpoint: '/upload', // Shrine's upload endpoint
})
}
return uppy
}
const uploadedFileData = (file, response, fileInput) => {
if (fileInput.dataset.uploadServer == 's3') {
const id = file.meta['key'].match(/^cache\/(.+)/)[1]; // object key without prefix
return JSON.stringify(fileData(file, id))
} else if (fileInput.dataset.uploadServer == 's3_multipart') {
const id = response.uploadURL.match(/\/cache\/([^\?]+)/)[1]; // object key without prefix
return JSON.stringify(fileData(file, id))
} else {
return JSON.stringify(response.body)
}
}
// constructs uploaded file data in the format that Shrine expects
const fileData = (file, id) => ({
id: id,
storage: 'cache',
metadata: {
size: file.size,
filename: file.name,
mime_type: file.type,
}
})
export { singleFileUpload, multipleFileUpload }
作物箱.js:
import 'cropperjs/dist/cropper.css';
import Cropper from 'cropperjs'
function cropbox(image, url, { onCrop }) {
image.src = url
new Cropper(image, {
aspectRatio: 1,
viewMode: 1,
guides: false,
autoCropArea: 1.0,
background: false,
zoomable: false,
crop: event => onCrop(event.detail)
})
}
export default cropbox
form.html.erb:
<div class="field">
<% f.label :title %>
<%= f.text_field :title %>
</div>
<div class="field">
<% f.label :content %>
<%= f.text_area :content %>
</div>
<div class="field ">
<% f.label :user_id %>
<%= f.hidden_field :user_id, value: current_user.id %>
</div>
<div class ="field form-group">
<%= f.fields_for :photos, Photo.new do |photos_fileds| %>
<%= photos_fileds.label :image , class: "form-control" %>
<%= photos_fileds.hidden_field :image, class: "upload-data", value:
photos_fileds.object.cached_image_data %>
<%= photos_fileds.file_field :image , class: "form-control ", id: "select-files"%><br/>
<div class="image-preview">
<img id="image" src="<%= photos_fileds.object.image_url(:medium) %>" height="300" class="rounded" >
</div>
<% end %>
</div>
<%= f.submit "create", class: "btn btn-primary" %>
<% end %>
应用程序.scss:
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
* vendor/assets/stylesheets directory can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
* files in this directory. Styles in this file should be added after the last require_* statement.
* It is generally better to create a new file per style scope.
*
*= ./bootstrap_import
*= require_tree .
*= require_self
*/
@import "bootstrap";
img {
display: block;
/* This rule is very important, please don't ignore this */
max-width: 100%;
}
.image-preview img {
display: block;
max-width: 100%;
}
.image-preview {
margin-bottom: 10px;
display: inline-block;
height: 300px;
}
img[src=""] {
visibility: hidden;
}